c++11 - 参数包之模版可变参数

本文详细介绍了C++11中的参数包,包括模板类中的模板参数包、函数模板的形参包、包展开的概念和应用场景。重点讲解了包展开的模式、在继承列表和初始化列表中的使用,以及在模板类实例化和函数调用中的应用。通过实例展示了参数包如何在构造函数和函数调用中被具体使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参数包

c++11 的参数包就是我们老版本c++中的可变参数。它的声明也很像我们老版本的可变参数

	template<class T, class ... Args>
	void Fun( T arg1,  Args);

参数包分为俩类

  1. 模版类中的 模版参数的参数包,它可以是一个具体的类型、模版类型。(其实就是带可变参数的模版类)。在模板类使用一个具体类型 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]
  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不可推导,因为形参推导顺序是从右到左。
可展开包的场景(可应用包展开)
  1. 函数调用
f(n, ++args...); // f(n, ++arg1, ++arg2);
  1. () 初始化时
Demo c1(args...); // Demo(arg1,arg2,arg3)
  1. {} 初始化 同2
  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> 
}
  1. 函数的形参列表
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]
  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);
};
  1. 继承列表与初始化列表
参数的具体使用列子

构造函数使用

    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)...);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值