1.通过模板递归和特化实现参数包展开
#include <iostream> #include <type_traits> #include <memory> using namespace std; // 【1】 template<typename... Types> struct Sum; // 【2】 template<typename First, typename... Rest> struct Sum<First, Rest...> { enum { value = Sum<First>::value + Sum<Rest...>::value }; }; // 【3】 template<typename Last> struct Sum<Last> { enum { value = sizeof(Last) }; }; int main() { // 【4】 std::cout << Sum<int, double, std::string>::value << std::endl; return 0; }
在上述代码中,一开始很不理解这个递归是怎么个调用法;现在想明白后觉得就应该如此。
【1】处的代码表示一个模板类的申明,这里说的申明和我们普通说的函数申明是一样的作用,一开始我没明白,认为是定义;
【2,3】处其实都是模板类的具体定义,我认为和函数的重载是一样的,不同参数,编译器会选择合适的具体模板定义;
在理解了上述两点后,瞬间觉得豁然开朗;
比如在【2】的代码段中,Sum<First>::value,编译器会去寻找【3】这个模板类的具体实现,如果不存在就会报value不存在等错误;
在【2】代码段中,Sum<Rest...>::value,因为这里的参数可以是1个,也可能是更多(不会是0个),因此会定位到【2】这个模板的具体实现,也就是自己,这里就实现了模板的递归;
2.通过模板继承和特化实现参数包展开
// 【1】
template<int...> struct IndexSeq{};
// 【2】 template<int N, int... Indexes> struct MakeIndexes : MakeIndexes<N - 1, N -1, Indexes...> {};
// 【3】 template<int... Indexes> struct MakeIndexes<0, Indeses...> { typedef IndexSeq<Indexes...> type; } int main() {
// 【4】 MakeIndexes<3>::type; return; }
【4】的展开过程如下:
MakeIndexes<3> - step1 -> MakeIndexes<2, 2> - step2 -> MakeIndexes<1, 1, 2> - step3 -> MakeIndexes<0, 0, 1, 2> - step4 -> IndexSeq<0, 1, 2>
其中step[1-3]都是使用【2】这个模板类通过继承的方式展开参数包;
MakeIndexes<3>中,N为3,Indexes为空;
MakeIndexes<2, 2>中,N为2, Indexes为2;
MakeIndexes<1, 1, 2>中,N为1, Indexes为1,2
MakeIndexes<0, 0, 1, 2>,就不再使用模板继承了,而是使用模板特化,Indexes为0, 1, 2,因此MakeIndexes<3>::type为IndexSeq<0, 1, 2>
书中轻描淡写的几句话,我花费了好长时间揣测出来;
继续,【当前的整形序列是升序的,如果降序只需修改模板继承定义如下,其他的不用变;】【看到这里我又陷入了沉思,之前的揣测是不是错了】
// 【2】
template<int N, int... Indexes> struct MakeIndexes : MakeIndexes<N - 1, Indexes..., N - 1> {};
MakeIndexes<3> - step1 -> MakeIndexes<2, 2> - step2 -> MakeIndexes<1, 2, 1> - step3 -> MakeIndexes<0, 2, 1, 0> - step4 -> IndexSeq<2, 1, 0>
同样,其中step[1-3]都是使用【2】这个模板类通过继承的方式展开参数包;
MakeIndexes<3>中,N为3, Indexes为空;
MakeIndexes<2, 2>中,N为2, Indexes为2;
MakeIndexes<1, 2, 1>中,N为1, Indexes为2, 1;
MakeIndexes<0, 2, 1, 0>,使用模板特化,Indexes为2, 1, 0,因此MakeIndexes<3>::type为IndexSeq<2, 1, 0>
原以为搞懂了可变参数模板类后,继续往下看,【使用上面生成的IndexSeq展开并打印可变模板参数,又看不懂了。。。】
template<int...> struct IndexSeq {}; template<int N, int... Indexes> struct MakeIndexes { using type = typename MakeIndexes<N - 1, N - 1, Indexes...>::type; }; template<int... Indexes> struct MakeIndexes<0, Indexes...> { using type = IndexSeq<Indexes...>; }; template<int... Indexes, typename... Args> void print_helper(IndexSeq<Indexes...>, std::tuple<Args...>&& tp) { print(std::get<Indexes>(tp)...); // 【1】【将tuple转换为函数参数,再调用方法1】 }
//【2】 template<typename... Args> void print(Args... args) { print_helper(typename MakeIndexes<sizeof...(Args)>::type(), std::make_tuple(args)); }
int main() {
print<int, double, std::string>(1, 2.5, "abc");
return 0;
}
在代码【1】处,我怎么也没想明白该怎么写,终于,明白这里应该是将参数打印出来的功能,而不是调用【2】处的模板函数,而是调用可变参数模板函数,如下:。
template<typename T> void print(T t) { std::cout << t << std::endl; } template<typename T, typename... Types> void print(T t, Types... args) { std::cout << t << std::endl; print(args...); }
过程大概就是这样了,现在明白后了觉得不值一提,但是还是记录一下。
只能感叹人与人的差距真的会比人与狗的差距还大。