-
Notifications
You must be signed in to change notification settings - Fork 0
CPlusPlus遍历模板参数包
c++11中引入了变长模板(variadic templates):template <typename ...T>
,由此可以向一个模板类传递任意长度、任意类型的类型参数。假设我们定义了一个接受变长模板参数的模板类,我们想检查传递进来的参数是否都是整数类型,那如何遍历这些参数进行一一检查呢?这里介绍两种方法。
通过模板特例化和模板递归,可以达到遍历的目的。首先定义一个一般化的版本,比如template <typename ...T> struct all_integral_t;
。它不负责具体的判断任务,具体的判断由特化的版本完成。然后定义一个递归形态的特化版本以及递归终止的特化版本:
#include <type_traits>
#include <iostream>
// Declaration
template <typename ...T>
struct all_integral_t;
// Termination
template <typename T>
struct all_integral_t<T> {
static constexpr bool value = std::is_integral<T>::value;
};
// Recursion
template <typename Head, typename ...Tails>
struct all_integral_t<Head, Tails...> {
static constexpr bool value = std::is_integral<Head>::value && all_integral_t<Tails...>::value;
};
它的原理是这样的:在Recursion特化版本中,把传递进来的模板参数分成Head和Tails两部分,首先检查Head是不是integral类型,然后递归调用all_integral_t<Tails...>
对Tails...部分进行检测。Tails...进而又被拆成Head'和Tails'...匹配给all_integral_t,此时Tails'比Tails就少了一个元素。重复下去,最终匹配到单参数的all_integral_t<T>
版本,递归结束。
可以拿这段代码测试:
int main() {
std::cout << all_integral_t<int>::value << std::endl; // true
std::cout << all_integral_t<int, short, long>::value << std::endl; // true
std::cout << all_integral_t<int, float, double>::value << std::endl; // false
}
boost::hana是一个元编程的库,它提供了异质容器,可以将类型不同的元素存放到同一个tuple中。hana还提供了操作tuple的各种接口,比如for_each, fold, filter, all_of等,可以让我们比较自然的访问tuple里的元素。通过下面一段简单的代码即可完成对变长模板的检查:
1 #include <boost/hana.hpp>
2
3 template <typename ...T>
4 struct all_integral_t {
5 static constexpr auto types = boost::hana::tuple_t<T...>;
6 static constexpr auto value = boost::hana::all_of(types, [](auto &&v) {
7 using t = typename std::remove_reference_t<decltype(v)>::type;
8 return std::is_integral<t>::value;
9 });
10 };
第5行使用了hana::tuple_t<T...>
来存储所有T...的信息,tuple_t
的定义如下:
template <typename ...T>
constexpr hana::tuple<hana::type<T>...> tuple_t{};
它是一个模板变量,即可以接受模板参数的变量。接受模板参数后tuple_t<T...>
是它的值,它的类型是hana::tuple<hana::type<T>...>
,也就是由类型为hana::type<T>...
的一系列变量为元素构成的tuple。暂时不管hana::type<T>
是如何定义的,只要知道它里面有一个type
成员记录了真实的T的类型即可。上述第6~9行就是遍历tuple里存放的所有元素,用一个lambda函数对每一个元素进行检查,第7行得到元素对应的T,用std::is_integral
检查是否是整数,当所有元素满足条件,hana::all_of
返回true。
介绍了两种遍历变长模板参数包的方法。其中第二种借用boost::hana库的类型变量的容器,可以像循环一样遍历参数包,比第一种递归方式更为自然。