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】处其实都是模板类的具体定义,我认为和函数的重载是一样的,不同参数,编译器会选择合适的具体模板定义;
在理解了上述两点后,瞬间觉得豁然开朗;
Sum<First>::value,编译器会去寻找【3】这个模板类的具体实现,如果不存在就会报value不存在等错误;
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为空;
2, 2>中,N为2, Indexes为2;
1, 1, 2>中,N为1, Indexes为1,2
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为空;
2, 2>中,N为2, Indexes为2;
1, 2, 1>中,N为1, Indexes为2, 1;
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...);
}
过程大概就是这样了,现在明白后了觉得不值一提,但是还是记录一下。
只能感叹人与人的差距真的会比人与狗的差距还大。