参数包
c++11 的参数包就是我们老版本c++中的可变参数。它的声明也很像我们老版本的可变参数
template<class T, class ... Args>
void Fun( T arg1, Args);
参数包分为俩类
- 模版类中的 模版参数的参数包,它可以是一个具体的类型、模版类型。(其实就是带可变参数的模版类)。在模板类使用一个具体类型 c++03也支持语法如下
具体类型我理解为 就是老版本中模版类如下的用法
template<class T, size_t N = 5 >
class Demo {
T [N];
};
模板可变参数使用一个具体类型的列子
template <class ...T>
void Fun(T... t) {}
// 展开为T t1, Tt2...
template<typename ...Ts, int... N> void g(Ts (&...arr)[N]) {}
int n[1];
g<const char, int>("a", n); // Ts (&...arr)[N] 展开成 // const char (&)[2], int(&)[1]
- 函数模版中的 形参 的参数包。其中类型可是一个模版类型也可以是一个具体类型。(其中就是带可变参数的模版函数)
参数包的参数
所有传递过来的类型,编译器都会在编译期的时候进行类型推导。参数包有个缺点,不能像 c 语言中的 va_list 那种可变参数,可以一个一个的使用相应可变参数。它只能使用包展开的方式 arg… ,经过包展开 arg… 会被替换成 arg1, arg2,arg3 这种形式的代码。
包展开
模式
包展开就像模板中的实例化,它可以由0个或多个参数构成一个参数包,并且使用统一的形式展开。
template<class ...Us> void f(Us... pargs) {}
template<class ...Ts> void g(Ts... args) {
f(&args...); // “&args...” 是包展开
// “&args” 是Ts 包展开的一个模式,每个实例化展开的参数都以这个模型来展开。
}
g(1, 0.2 , "a"); // Us : int, double, const char* Ts: int*, double*, const char**
处于同一模式中的俩个包
处于同一模型中的俩个包,它们的长度必须相同
template<typename...> struct Test {};
template<typename T1, typename T2> struct Pair {};
template<class ...Args1> struct Outer {
template<class ...Args2> struct Inner {
typedef Tuple<Pair<Args1, Args2>...> type;
// Pair<Args1, Args2>... 是包展开
// Pair<Args1, Args2> 是模式
};
};
typedef Outer<short, int>::Inner<unsigned short, unsigned>::type T1;
// Pair<Args1, Args2>... 展开如下
// Pair<short, unsigned short>, Pair<int, unsigned int>
// T1 是 Tuple<Pair<short, unsigned short>, Pair<int, unsigned>>
typedef Outer<short>::Inner<unsigned short, unsigned>::type T2;
// 错误:处于同一模式的俩个包长度不同
模版类的参数包
参数列表
模版类的参数包(可变参数),必须是模版参数列表中的最后一个。使用可变参数的模版类,可以使用任意数量的参数来实例化,也就是可变参数可以是0个 也可以是 多个。
template <class ... Types>
struct Valid {};
template <class ...Types , class B>
struct InValid; // 非法
Valid<>v; // 0 个
Valid<int> v2; // 1个 int
继承列表和初始化列表
因为包展开可以应用于 继承列表,那么为了初始化相应的基类,我们需要使用初始化列表的包展开初始化
template <class... Types>
class Demo : public T {
public:
X(const T&... ts)
: T(ts)...
{}
模版的参数包
模版的参数包只应用于形参列表,但是它不同于模版类,它的可变参数(参数包),不是必须位于最后一个形参,它可以在 可推导的形参、缺省参数前。
template <class ...Types, class U>
void Valid( U , Ts...); // OK : U 可推导
void InValid(Ts..., U); // 错误,U不可推导,因为形参推导顺序是从右到左。
可展开包的场景(可应用包展开)
- 函数调用
f(n, ++args...); // f(n, ++arg1, ++arg2);
- () 初始化时
Demo c1(args...); // Demo(arg1,arg2,arg3)
- {} 初始化 同2
- 模版类的实例化
template<class A, class B, class...C> void func(A arg1, B arg2, C...arg3)
{
container<A,B,C...> t1; // 展开成 container<A,B,E1,E2,E3>
container<C...,A,B> t2; // 展开成 container<E1,E2,E3,A,B>
container<A,C...,B> t3; // 展开成 container<A,E1,E2,E3,B>
}
- 函数的形参列表
template <class ...T>
void Fun(T... t) {}
// 展开为T t1, Tt2...
template<typename ...Ts, int... N> void g(Ts (&...arr)[N]) {}
int n[1];
g<const char, int>("a", n); // Ts (&...arr)[N] 展开成 // const char (&)[2], int(&)[1]
- 模版参数列表
template <class ... Types>
class Outer {
template <Types... t>
class Inner {};
};
7.sizeof … , 它表示的是包的大小,不是包元素的总和大小
template<class... Types>
struct count {
static const std::size_t value = sizeof...(Types);
};
- 继承列表与初始化列表
参数的具体使用列子
构造函数使用
template<class... ARGS>
T* acquire(ARGS&&... args) {
new (x) T(std::forward<ARGS>(args)...);
return x;
}
调用函数的时候使用
template <class ... ARGS>
void fun(ARGS&& ... args) {
fun(std::forward<ARGS>(args)...);
}