• 可变参数模板类


    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...);
    }

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

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

  • 相关阅读:
    Jquery中addClass方法不起作用的解决方案
    JavaScript 下拉框 左边添加至右边
    电商网站常用放大镜特效
    jQuery 移除事件与模拟事件
    考勤管理系统V1.0.3
    js 严格模式
    service workder
    本地存储之application cache和localstorage
    Node.js内置的文件系统模块(fs)
    Node.js:OS模块
  • 原文地址:https://www.cnblogs.com/sip-inaction/p/13748407.html
Copyright © 2020-2023  润新知