可变参数模板类

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>(1, 2.5, "abc") << std::endl;

    return 0;
}

    在上述代码中,一开始很不理解这个递归是怎么个调用法;现在想明白后觉得就应该如此。

    【1】处的代码表示一个模板类的申明,这里说的申明和我们普通说的函数申明是一样的作用,一开始我没明白,认为是定义;

    【2,3,4】处其实都是模板类的具体定义,我认为和函数的重载是一样的,不同参数,编译器会选择合适的具体模板定义;

    在理解了上述两点后,瞬间觉得豁然开朗;

        比如在【2】的代码段中,Sum<First>::value,编译器回去寻找【3】这个模板类的具体实现,如果不存在就会报value不存在等错误;

        在【2】代码段中,Sum<Rest...>::value,因为这里的参数可以是1个,也可能是更多,因此会定位到【2】这个模板的具体实现,也就是自己,这里就实现了模板的递归;

 

2.通过模板继承和特化实现参数包展开

// 【1】
template<int...> IndexSeq{};
// 【2】 template<int N, int... Indexes> MakeIndexes : MakeIndexes<N - 1, N -1, Indexes...> {};
// 【3】 template<int... Indexes> 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> 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 = 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)); }

    在代码【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...);
}

    过程大概就是这样了,现在明白后了觉得不值一提,但是还是记录一下。

 

 

 

只能感叹人与人的差距真的会比人与狗的差距还大。

 

上一篇:ArcGIS Runtime SDKs v10.2.4最新(Android、iOS、OSX和.NET)


下一篇:Postgresql数据库介绍10——使用