C++11新特性:变长模版
该内容总结自深入理解C++11:C++11新特性解析与应用
变长函数
变长函数是c语言的一个特性,使得函数能接受任何长度的参数列表(例如printf函数)
#include <stdio.h>
#include <stdarg.h>
double SumOfFloat(int count, ...){
va_list ap;
double sum = 0;
va_start(ap, count);
for(int i = 0; i < count; i++)
sum += va_arg(ap, double);
va_end(ap);
return sum;
}
这段代码是一个简单的变长函数的例子,函数须传入参数数量count,在函数体内通过va_start对va_list类型的变量ap做初始化,后续代码使用va_arg函数将参数一一取出。
变长模版:模版参数包和函数参数包
模版参数包
语法
// 类型
template <typename... Elements> class ClassName{};
ClassName<int, char, double> cn;
// 非类型
template<int... A> class NonTypeTemplate{};
NonTypeTemplate<1, 0, 2> ntvt;
在c++11中,Elements、A就是所谓的模版参数包。
应用
为了使用模板参数包,在类中需要将其解包(unpack),通过一个名为包扩展(pack expansion的表达式来实现。
//定义
template<typename T1, typename T2> class B{};
template<typename... A> class Template: private B<A...>{};//包扩展A...
//使用: X, Y -> A -> A... -> X, Y
Template<X, Y> xy;
这个例子是基于Template内中对A…的使用总是接受两个参数的前提下,如果使用Template<X, Y, Z>就会发生模版推导的错误。
那么就可以通过递归进一步实现接受任意多的模版参数,通过简化tuple实现来举例。
//递归实现接受任意多的模版参数
//简化tuple实现举例
template <typename... Elements> class tuple; //变长模版的声明
template<typename Head, typename... Tail> //递归的偏特化定义
class tuple<Head, Tail...>: private tuple<Tail...>{
Head head;
};
template<> class tuple<>{}; //边界条件,结束递归
tuple<double, int, char, float> tp;
这段代码可以简单理解,我调用<double, int, char, float>,在第一次时double作为head,<int, char, float>作为模版参数包继续递归传递,第二次int作为head,<char, float>继续传递,以此类推,最后实现一个包含<double, int, char, float>四个类型的tuple tp,通过下图可以更好理解这么一个原理。

函数参数包
语法
template<typename... T> void f(T... args); //函数参数包:args
在C++11中,函数参数包有两个限制
- 唯一
- 函数的最后一个参数
ps:函数参数包和模版参数包其他差不多,故不做赘述
应用
#include <iostream>
// 基础函数,用于终止递归
void print() {
std::cout << std::endl;
}
// 可变参数模板函数
template<typename T, typename... Args>
void print(T first, Args... args) {
std::cout << first << " ";
print(args...); // 递归调用
}
int main() {
print(1, 2.5, "Hello", 'a'); // 输出: 1 2.5 Hello a
print("C++", 11, 14, 17); // 输出: C++ 11 14 17
return 0;
}
变长模版:进阶
上面例子中,参数包在基类描述列表和函数体内做了展开,c++规定了7种参数包可以展开的位置
- 表达式
- 初始化列表
- 基类描述列表
- 类成员初始化列表
- 模版参数列表
- 通用属性列表
ps:这些都是什么不在这里做说明,可以自行了解
// B<A>...
template<typename... Ts>
struct TupleOfB {
std::tuple<B<Ts>...> data; // 展开为 B<T1>, B<T2>, ..., B<Tn>
};
TupleOfB<int, float, char> t; // 等价于 std::tuple<B<int>, B<float>, B<char>>
//B<A...>
template<typename... Args>
struct B {};
template<typename... As>
struct Foo {
B<As...> b; // 把 As... 的所有参数一次性传给 B
};
Foo<int, float, char> f; // 等价于 B<int, float, char>
int size = sizeof...(A); //计算变长包的长度
template<typename T, typename... Args>
T create(Args&&... args) {
// 完美转发参数给构造函数
return T(std::forward<Args>(args)...);
}
class MyClass {
public:
MyClass(int a, double b, const std::string& c) {
std::cout << "Constructed with: " << a << ", " << b << ", " << c << "\n";
}
};

3904

被折叠的 条评论
为什么被折叠?



