模板增强
一、外部模板
-
传统 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 表达式,完成参数的输出。
-