右尖括号 > 改进
在 C++ 98/03 的泛型编程中,模板实例化有一个很繁琐的地方,就是连续两个右尖括号>>
会被编译解释成右移操作符,而不是模板参数表的形式,需要一个空格进行分割,以避免发生编译时的错误。
vector<vector<int>> a;// err, 编译失败
vector<vector<int> > a; // ok, 编译成功
在实例化模板时会出现连续两个右尖括号,同样 static_cast
、dynamic_cast
、reinterpret_cast
、const_cast
表达式转换时也会遇到相同的情况。C++ 98 标准是让程序员在 >>
之间填上一个空格,在 C++ 11中,这种限制被取消了。在 C++ 11标准中,要求编译器对模板的右尖括号做单独处理,使编译器能够正确判断出 >>
是一个右移操作符还是模板参数表的结束标记。
模板的别名
对于冗长或者复杂的标识符,C++ 提供了 typedef
用来创建其别名
//原来的C++
typedef vector<string>::iterator itType;
//C++ 11
using itType = vector<string>::iterator;
二者的差别在于,新语法也可以用于模板部分的具体化,但是 typedef
不能
template<typename T>
using arr12 = array<T, 12>;
//默认的使用方法
array<double, 12> a1;
array<string, 12> a2;
//替换为
arr12<double> b1;
arr12<string> b2;
函数模板的默认模板参数
C++11之前,类模板是支持默认的模板参数,却不支持函数模板的默认模板参数:
//1、普通函数带默认参数,c++98 编译通过,c++11 编译通过
void DefParm(int m = 3) {}
//2、类模板是支持默认的模板参数,c++98 编译通过,c++11 编译通过
template <typename T = int>
class DefClass {};
//3、函数模板的默认模板参数, c++98 - 编译失败,c++11 - 编译通过
template <typename T = int> void DefTempParm() {}
类模板的默认模板参数必须从右往左定义,函数模板的默认模板参数则没这个限定:
template<class T1, class T2 = int> class DefClass1;
template<class T1 = int, class T2> class DefClass2; // 无法通过编译
template<class T, int i = 0> class DefClass3;
template<int i = 0, class T> class DefClass4; // 无法通过编译
template<class T1 = int, class T2> void DefFunc1(T1 a, T2 b);
template<int i = 0, class T> void DefFunc2(T a);
变长参数模板
在 C++ 11 之前,类模板和函数模板只能含有固定数量的模板参数。C++ 11 增强了模板功能,允许模板定义中包含 0 到任意个模板参数,这就是可变参数模板。
可变参数模板和普通模板的语义是一样的,只是写法上稍有区别,声明可变参数模板时需要在 typename
或 class
后面带上省略号 …
:
#include <iostream>
using namespace std;
template<typename... T>
void func(T ... args) {
}
int main() {
func(); // OK:args不含有任何实参
func(1); // OK:args含有一个实参:int
func(1, 1.1); // OK:args含有两个实参int和double
return 0;
}
T
叫模板参数包,args
叫函数参数包。
省略号 …
的作用有两个:
- 声明一个参数包,这个参数包中可以包含0到任意个模板参数
- 在模板定义的右边,可以将参数包展开成一个一个独立的参数
可变参数模板函数的定义
一个可变参数模板函数的定义如下:
#include <iostream>
using namespace std;
template<typename... T>
void func(T ... args) {
cout << "num = " << sizeof...(args) << endl;
}
int main() {
func();// num = 0
func(1);// num = 1
func(1, 1.1);// num = 2
return 0;
}
参数包的展开
递归方式展开
通过递归函数展开参数包,需要提供一个参数包展开的函数和一个递归终止函数。
void print() {
cout << "empty" << endl;
}
template<class T, class... Args>
void print(T first,Args ... args) {
cout << "parameter " << first << endl;
print(args...);
}
int main() {
print(1, 2, 3.3, 'c', "test");
return 0;
}
运行结果如下:
递归调用过程如下:
print(1, 2, 3.3, 'c', "test");
print(2, 3.3, 'c', "test");
print(3.3, 'c', "test");
print( 'c', "test");
print("test");
print();
通过可变参数模板实现打印函数:
#include <iostream>
#include <vector>
using namespace std;
void Print(const char* s) {
while (*s) {
if (*s == '%' && *++s != '%') {
throw runtime_error("invalid format string: missing arguments");
}
cout << *s++;
}
}
template<class T, class... Args>
void Print(const char* s, T value, Args... args) {
while (*s) {
if (*s == '%' && *++s != '%') {
cout << value;
return Print(++s, args...);
}
cout << *s++;
}
throw runtime_error("extra arguments provided to Debug");
}
int main() {
Print("a = %d, b = %c, c = %s\n", 250, 't', "test");
return 0;
}
非递归方式展开
template<class T>
void Print(T arg) {
cout << arg << endl;
}
template<class... Args>
void expand(Args... args) {
int a[] = { (Print(args), 0)... };
}
int main() {
expand("a = %d, b = %c, c = %s\n", 250, 't', "test");
return 0;
}
可变参数模板类
补课ing...