C++11/14介绍(二)——语言可用性的强化(五)

模板增强

一、外部模板

  • 传统 C++ 中,模板只有在使用时才会被编译器实例化。换句话说,只要在每个编译单元(文件)中编译的代码中遇到了被完整定义的模板,都会实例化。这就产生了重复实例化而导致的编译时间的增加。并且,我们没有办法通知编译器不要触发模板实例化。

  • C++11 引入了外部模板,扩充了原来的强制编译器在特定位置实例化模板的语法,使得能够显式的告诉编译器何时进行模板的实例化:

    template class std::vector<bool>;            // 强行实例化
    extern template class std::vector<double>;  // 不在该编译文件中实例化模板
    
  • C++11 开始,连续的右尖括号将变得合法,并且能够顺利通过编译。

    std::vector<std::vector<int>> wow;
    

二、类型别名模板

  • 模板是用来产生类型的,传统C++中,typedef可以为类型定义一个新的名称,但是没有办法为模板定义一个新的名称。因为模板不是类型

    template< typename T, typename U, int value>
    class SuckType {
    public:
        T a;
        U b;
        SuckType():a(value),b(value){}
    };
    template< typename U>
    typedef SuckType<std::vector<int>, U, 1> NewType; // 不合法
    
  • C++11 使用 using 引入了下面这种形式的写法,并且同时支持对传统 typedef 相同的功效

    typedef int (*process)(void *);  // 定义了一个返回类型为 int,参数为 void* 的函数指针类型,名字叫做 process
    using process = int(*)(void *); // 同上, 更加直观
    
    template <typename T>
    using NewType = SuckType<int, T, 1>;    // 合法
    

三、默认模板参数

  • C++编写函数时,可以指定默认参数,ex:

    void test(int i = 0)
    {
        cout<<i;
    }
    int main()
    {
        test();
        return 0;
    }
    
  • C++11中提供了可以指定模板的默认参数:

    template<typename T = int, typename U = int>
    auto add(T x, U y) -> decltype(x+y) {
        return x+y;
    }
    

四、变长参数模板

  • C++11 加入了新的表示方法,允许任意个数、任意类别的模板参数,同时也不需要在定义时将参数的个数固定。

    template<typename... Ts> class Magic;
    

    模板类Magic的对象,可以接受不限制个数的typename作为模板的形式参数,ex:

    template<typename... Ts> class Magic;
    class Magic<int,vector<int>,map<string,vector<int>>> *darkMagic;
    int main()
    {
        return 0;
    }
    
    

    个数为0的模板参数也是可以的:

    class Magic<> *nothing;
    

    如果不希望产生的模板参数个数为0,可以手动的定义至少一个模板参数:

    template<typename Require, typename... Args> class Magic;
    
  • 定义了变长的模板参数,如何对参数进行解包呢

  • 首先可以使用sizeof...计算参数的个数

    template<typename... Args>
    void magic(Args... args) {
        cout << sizeof...(args) << std::endl;
    }
    int main()
    {
        magic();        // 输出0
        magic(1);       // 输出1
        magic(1, "");   // 输出2
        return 0;
    }
    
  • 其次,对参数进行解包,有以下两种经典的处理手法:

    • 递归模板函数

      template<typename T>
      void printf(T value) {
          cout << value <<endl;
      }
      template<typename T, typename... Args>
      void printf(T value, Args... args) {
          cout << value << endl;
          printf(args...);
      }
      int main() {
          printf(1, 2, "123", 1.1);
          return 0;
      }
      
      

      这种方法不断递归的向函数传递模板参数,进而达到递归遍历所有模板参数的目的

    • 初始化列表展开

      // 编译这个代码需要开启 -std=c++14
      template<typename T>
      void printarg(T a){
          cout << a << endl;
      }
      
      template<class...Args>
      auto printmsg(Args... args){
          return initializer_list<int>{(printarg(args), 0)...}; 
          //return initializer_list<int>{([&]{cout << args << endl;}(), 0)...}; 
          //lambda表达式,编译出错的话参考下面网址
          //https://stackoverflow.com/questions/14368744/does-lambda-capture-support-variadic-template-arguments
      }
      //{(printarg(args), 0)...}会被展开为{(printarg(arg1), 0), (printarg(arg2), 0), (printarg(arg3), 0)}
      int main() {
          printmsg(1,2,3,"hello",2.65);
          return 0;
      }
      

      通过初始化列表,(lambda 表达式, value)... 将会被展开。由于逗号表达式的出现,首先会执行前面的 lambda 表达式,完成参数的输出。

  • 参考:C++ 11/14 高速上手教程 - 语言可用性的强化

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值