C++11中的可变长模板参数
一、引言
C++11 引入了变长模板(Variadic Templates),这是模板编程领域的一个重大改进,它允许模板参数的数量是可变的。这一特性极大地增强了模板的灵活性和表达能力,使得开发者能够编写更加通用和强大的代码。变长模板主要通过模板参数包(Template Parameter Packs)和模板函数包(Template Function Packs)来实现。
二、基本语法
2.1 模板参数包
模板参数包允许模板接受任意数量和类型的模板参数。在模板定义中,使用省略号(...)来指示参数包。参数包可以包含类型参数或值参数。
2.2 类型参数包
类型参数包用于模板类或模板函数中,表示可以接受任意数量的类型参数。
template<typename... Types>
class Tuple {
// ...
};
// 使用示例
Tuple<int, double, std::string> t;
在这个例子中,Tuple 类模板接受一个类型参数包 Types,它可以包含任意数量的类型。
2.3 值参数包
值参数包用于模板函数中,表示可以接受任意数量和类型的函数参数。
template<typename... Args>
void print(Args... args) {
// 使用递归模板函数或初始化列表展开参数包
// 这里仅作为示例,实际实现需要更复杂的逻辑
}
// 使用示例
print(1, 3.14, "Hello");
三、模板参数的展开
方式1:递归展开
#include <iostream>
using namespace std;
// 特例函数,只有一个参数时调用
template <typename T>
void print(const T& item){
cout << '[' << item << ']' << endl;
}
// 通用函数
template <typename T, typename... Args>
void print(const T& item, Args... args){
cout << '[' << item << ']';
print(args...); // 递归调用
}
int main(){
print(1, 2.5, "Hello", 'Z'); // 输出:[1][2.5][Hello][Z]
return 0;
}
展开过程:①:print(1, 2.5, "Hello", 'Z') 匹配通用函数,输出 [1] ,再调用 print(2.5, "Hello", 'Z')
②:print(2.5, "Hello", 'Z') 匹配通用函数,输出 [2.5] ,再调用 print("Hello", 'Z')
③:print("Hello", 'Z') 匹配通用函数,输出 [Hello] ,再调用 print( 'Z')
④:print('Z') 匹配特例函数,输出 [Z] ,结束递归。
方式2:初始化列表展开
#include <iostream>
using namespace std;
template <typename... Args>
void print(Args... args){
(void)initializer_list<int> {
(cout << '[' << args << ']', 0)...
};
cout << endl;
}
int main(){
print(1, 2.5, "Hello", 'Z'); // 输出:[1][2.5][Hello][Z]
return 0;
}
展开过程:
initializer_list<int>用于初始化一个int类型的列表,(cout << '[' << args << ']', 0)是一个逗号表达式,逗号表达式的优先级最低,其表达式的值为逗号右侧表达式的值,最终得到一个数量和args相同且值都为0的int类型列表,并且将模板参数展开。(void)是为了不产生变量未使用的警告。
方式3:enable_if表达式结合tuple类型展开
#include <iostream>
#include <tuple>
#include <type_traits>
using namespace std;
// 特例函数:当 k 的值和t中元素个数相等时,结束递归
template <size_t k = 0, typename Tuple>
typename enable_if<k == tuple_size<Tuple>::value>::type
format_tuple(const Tuple& t){
cout << endl;
}
// 通用函数:当 k 的值小于t中元素个数时,递归调用,输出tuple中的下一个元素
template <size_t k = 0, typename Tuple>
typename enable_if<k < tuple_size<Tuple>::value>::type
format_tuple(const Tuple& t){
cout << '[' << get<k>(t) << ']';
format_tuple<k + 1>(t);
}
template <typename... Args>
void print(Args... args){
format_tuple(make_tuple(args...));
}
int main(){
print(1, 2.5, "Hello", 'Z'); // 输出:[1][2.5][Hello][Z]
return 0;
}
展开过程:①:print(1, 2.5, "Hello", 'Z') 调用format_tuple<0>( tuple(1, 2.5, "Hello", 'Z') ),
因为k为0,小于元素的长度4,匹配通用函数,输出[1],然后递归调用
format_tuple<1>( tuple(1, 2.5, "Hello", 'Z') )
②:format_tuple<1>( tuple(1, 2.5, "Hello", 'Z') ),1 < 4 输出 [2.5] ,再调用
format_tuple<2>( tuple(1, 2.5, "Hello", 'Z') )
③:format_tuple<2>( tuple(1, 2.5, "Hello", 'Z') ),2 < 4 输出 [Hello] ,再调用
format_tuple<3>( tuple(1, 2.5, "Hello", 'Z') )
④:format_tuple<3>( tuple(1, 2.5, "Hello", 'Z') ),3 < 4 输出 [Z] ,再调用
format_tuple<4>( tuple(1, 2.5, "Hello", 'Z') )
⑤:format_tuple<4>( tuple(1, 2.5, "Hello", 'Z') ),4 == 4,输出endl ,递归结束。
四、小结
变长模板是 C++11 引入的一项强大功能,它极大地扩展了模板编程的灵活性和表达能力,使得开发者能够编写出更加通用和强大的代码。
附:c++11新增的其他特性