C++11 函数模板的默认模板参数

类模板:通用的类描述(使用泛型来定义类),进行实例化时,其中的泛型再用具体的类型替换。

函数模板:通用的函数描述(使用泛型来定义函数),进行实例化时,其中的泛型再用具体的类型替换。

【1】C++98标准中两者的区别

函数模板和类模板在C++98标准中一起被引入,两者区别主要在于:

在类模板声明时,标准允许其有默认模板参数。而函数模板却不支持。

默认模板参数的作用如同函数的默认形参。不过在C++11中,这一限制已经被解除了,如下例所示:

1 void DefParm(int m = 3) {} // c++98编译通过,c++11编译通过
2 
3 template <typename T = int>
4 class DefClass {};        // c++98编译通过,c++11编译通过
5 
6 template <typename T = int>
7 void DefTempParm() {};    // c++98编译失败,c++11编译通过

可以看到,DefTempParm函数模板拥有一个默认模板参数(类型int)。

使用仅支持C++98的编译器编译,DefTempParm的编译会失败,而支持C++11的编译器则无问题。

【2】C++11标准中两者的区别

尽管C++11支持了函数模板的默认模板参数,不过在语法上,两者还是存在区别:

类模板在为多个默认模板参数声明指定默认值时,必须遵照“从右往左”的规则进行指定。

而这个规则对函数模板来说并不是必须的。示例如下:

 1 template <typename T1, typename T2 = int>
 2 class DefClass1 {};
 3 
 4 template <typename T1 = int, typename T2>
 5 class DefClass2 {};  // ERROR: 无法通过编译:因为模板参数的默认值没有遵循“由右往左”的规则
 6 
 7 template <typename T, int i = 0>
 8 class DefClass3 {};
 9 
10 template <int i = 0, typename T>
11 class DefClass4 {};  // ERROR: 无法通过编译:因为模板参数的默认值没有遵循“由右往左”的规则
12 
13 template <typename T1 = int, typename T2>
14 void DefFunc1(T1 a, T2 b) {}; // OK 函数模板不用遵循“由右往左”的规则
15 
16 template <int i = 0, typename T>
17 void DefFunc2(T a) {};  // OK 函数模板不用遵循“由右往左”的规则

可以看到,不按照从右往左定义默认类模板参数的模板类DefClass2和DefClass4都无法通过编译。

而对于函数模板来说,默认模板参数的位置则比较随意。

DefFunc1和DefFunc2都为第一个模板参数定义了默认参数,而第二个模板参数的默认值并没有定义,C++11编译器却认为没有问题。

函数模板的参数推导规则也并不复杂。简单地讲:如果能够从函数实参中推导出类型的话,那么默认模板参数就不会被使用,反之,默认模板参数则可能会被使用。

如下示例:

 1 template <class T, class U = double>
 2 void f(T t = 0, U u = 0) {};
 3 void g() 
 4 {
 5     f(1, 'c'); // f<int, char>(1, 'c') 
 6     f(1);      // f<int, double>(1, 0), 使用了默认模板参数double
 7     f();       // 错误: T无法被推导出来
 8     f<int>();  // f<int, double>(0, 0), 使用了默认模板参数double
 9     f<int, char>(); // f<int, char>(0, 0)
10 }

定义了一个函数模板f,f同时使用了默认模板参数和默认函数参数。

可以看到,由于函数的模板参数可以由函数的实参推导而出:

在f(1)这个函数调用中,实例化出了模板函数的调用应该为f<int, double>(1, 0),其中,第二个类型参数U使用了默认的模板类型参数double,而函数实参则为默认值0。

类似地,f<int>()实例化出的模板函数第二参数类型为double,值为0。

而表达式f()由于第一类型参数T的无法推导,从而导致了编译的失败。

而通过这个例子也可以看到,默认模板参数通常是需要跟默认函数参数一起使用的。

还有一点应该注意:模板函数的默认形参值不是模板参数推导的依据。函数模板参数的选择,终究是由函数的实参推导而来的。

good good study, day day up.

顺序 选择 循环 总结

原文地址:https://www.cnblogs.com/Braveliu/p/12231451.html