首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Parameter pack

模板参数包是接受零或多个模板参数%28非类型、类型或模板%29的模板参数。函数参数包是接受零或多个函数参数的函数参数。

具有至少一个参数包的模板称为可变模板...

句法

模板参数Pack%28出现在类模板在一个功能模板参数列表%29。

type ... Args(optional)

(1)

(since C++11)

typename|class ... Args(optional)

(2)

(since C++11)

template < parameter-list > typename(C++17)|class ... Args(optional)

(3)

(since C++11)

函数参数包%28a形式报关员出现在变量函数模板%29的函数参数列表中。

Args ... args(optional)

(4)

(since C++11)

参数包展开%28出现在变量模板%29的主体中。

pattern ...

(5)

(since C++11)

1%29带有可选名称的非类型模板参数包。

2%29带有可选名称的类型模板参数包

3%29模板参数包具有可选名称

4%29带有可选名称的函数参数包

5%29参数包展开:展开为逗号分隔的零或更多列表pattern模式必须至少包括一个参数包。

解释

可以用任意数量的模板参数实例化各种类模板:

二次

代码语言:javascript
复制
template<class ... Types> struct Tuple {};
Tuple<> t0;           // Types contains no arguments
Tuple<int> t1;        // Types contains one argument: int
Tuple<int, float> t2; // Types contains two arguments: int and float
Tuple<0> error;       // error: 0 is not a type

二次

可以用任意数量的函数参数%28调用变量函数模板。模板参数推导29%:

二次

代码语言:javascript
复制
template<class ... Types> void f(Types ... args);
f();       // OK: args contains no arguments
f(1);      // OK: args contains one argument: int
f(2, 1.0); // OK: args contains two arguments: int and double

二次

在主类模板中,模板参数包必须是模板参数列表中的最终参数。在函数模板中,模板参数包可能会出现在列表的前面,条件是可以从函数参数中推导出以下所有参数,或者具有默认参数:

二次

代码语言:javascript
复制
template<typename... Ts, typename U> struct Invalid; // Error: Ts.. not at the end
 
template<typename ...Ts, typename U, typename=void>
void valid(U, Ts...);     // OK: can deduce U
// void valid(Ts..., U);  // Can't be used: Ts... is a non-deduced context in this position
 
valid(1.0, 1, 2, 3);      // OK: deduces U as double, Ts as {int,int,int}

二次

包装膨胀

后面跟着省略号的一种模式,其中至少一个参数包的名称至少出现一次。扩大转换为模式的零个或多个逗号分隔的实例化,其中参数包的名称按顺序被包中的每个元素替换。

二次

代码语言:javascript
复制
template<class ...Us> void f(Us... pargs) {}
template<class ...Ts> void g(Ts... args) {
    f(&args...); // “&args...” is a pack expansion
                 // “&args” is its pattern
}
g(1, 0.2, "a"); // Ts... args expand to int E1, double E2, const char* E3
                // &args... expands to &E1, &E2, &E3
                // Us... pargs expand to int* E1, double* E2, const char** E3

二次

如果两个参数包的名称以相同的模式出现,则它们同时展开,并且必须具有相同的长度:

二次

代码语言:javascript
复制
template<typename...> struct Tuple {};
template<typename T1, typename T2> struct Pair {};
 
template<class ...Args1> struct zip {
    template<class ...Args2> struct with {
        typedef Tuple<Pair<Args1, Args2>...> type;
//        Pair<Args1, Args2>... is the pack expansion
//        Pair<Args1, Args2> is the pattern
    };
};
 
typedef zip<short, int>::with<unsigned short, unsigned>::type T1;
// Pair<Args1, Args2>... expands to
// Pair<short, unsigned short>, Pair<int, unsigned int> 
// T1 is Tuple<Pair<short, unsigned short>, Pair<int, unsigned>>
 
typedef zip<short>::with<unsigned short, unsigned>::type T2;
// error: pack expansion contains parameter packs of different lengths

二次

如果一个包展开嵌套在另一个包扩展中,则它将展开显示在最里面的包展开中的参数Pack,并且必须在封装包展开中提到另一个包,但在最内部的包中则不能这样做:

二次

代码语言:javascript
复制
template<class ...Args>
    void g(Args... args) {
        f(const_cast<const Args*>(&args)...); 
 // const_cast<const Args*>(&args) is the pattern, it expands two packs
 // (Args and args) simultaneously
 
        f(h(args...) + args...); // Nested pack expansion:
   // inner pack expansion is "args...", it is expanded first
   // outer pack expansion is h(E1, E2, E3) + args..., it is expanded
   // second (as h(E1,E2,E3) + E1, h(E1,E2,E3) + E2, h(E1,E2,E3) + E3)
}

二次

扩张位点

根据展开发生的位置,结果的逗号分隔列表是不同类型的列表:函数参数列表、成员初始化程序列表、属性列表等。下面是所有允许的上下文列表。

函数参数列表

函数调用操作符的括号中可能出现一个包展开,在这种情况下,省略号左边的最大表达式是展开的模式。

二次

代码语言:javascript
复制
f(&args...); // expands to f(&E1, &E2, &E3)
f(n, ++args...); // expands to f(n, ++E1, ++E2, ++E3);
f(++args..., n); // expands to f(++E1, ++E2, ++E3, n);
f(const_cast<const Args*>(&args)...);
// f(const_cast<const E1*>(&X1), const_cast<const E2*>(&X2), const_cast<const E3*>(&X3))
f(h(args...) + args...); // expands to 
// f(h(E1,E2,E3) + E1, h(E1,E2,E3) + E2, h(E1,E2,E3) + E3)

二次

模板参数列表

可以在模板参数列表中的任何地方使用Pack展开,只要模板具有匹配展开的参数。

二次

代码语言:javascript
复制
template<class A, class B, class...C> void func(A arg1, B arg2, C...arg3)
{
    container<A,B,C...> t1;  // expands to container<A,B,E1,E2,E3> 
    container<C...,A,B> t2;  // expands to container<E1,E2,E3,A,B> 
    container<A,C...,B> t3;  // expands to container<A,E1,E2,E3,B> 
}

二次

功能参数表

在函数参数列表中,如果省略号出现在参数声明%28中,它是否在参数包%28中指定函数参数包,args...ARGS%29或非%29参数声明是模式:

二次

代码语言:javascript
复制
template<typename ...Ts> void f(Ts...) {}
f('a', 1);  // Ts... expands to void f(char, int)
f(0.1);     // Ts... expands to void f(double)
 
template<typename ...Ts, int... N> void g(Ts (&...arr)[N]) {}
int n[1];
g<const char, int>("a", n); // Ts (&...arr)[N] expands to 
                            // const char (&)[2], int(&)[1]

二次

注:在模式中Ts (&...arr)[N],省略号是最内部的元素,而不是所有其他包展开中的最后一个元素。

注:Ts (&...)[N]不允许使用,因为C++11语法要求括号大小的省略号有一个名称:CWG#1488...

模板参数列表

Pack展开可能出现在模板参数列表中:

二次

代码语言:javascript
复制
template<typename... T> struct value_holder
{
    template<T... Values> // expands to a non-type template parameter 
    struct apply { };     // list, such as <int, char, int(&)[5]>
};

二次

基说明符和成员初始化程序列表

包展开可以指定类声明通常,这也意味着构造函数需要在成员初始化列表调用这些基的构造函数:

二次

代码语言:javascript
复制
template<class... Mixins>
class X : public Mixins... {
 public:
    X(const Mixins&... mixins) : Mixins(mixins)... { }
};

二次

带括号的init列表

在大括号内列表%28大括号内的初始化程序列表和其他大括号-init-列表中,用于列表初始化在其他一些上下文中,还可能出现包扩展:

二次

代码语言:javascript
复制
template<typename... Ts> void func(Ts... args){
    const int size = sizeof...(args) + 2;
    int res[size] = {1,args...,2};
    // since initializer lists guarantee sequencing, this can be used to
    // call a function on each element of a pack, in order:
    int dummy[sizeof...(Ts)] = { (std::cout << args, 0)... };
}

二次

兰伯达

参数包可能出现在兰卜达表情。

二次

代码语言:javascript
复制
template<class ...Args>
void f(Args... args) {
    auto lm = [&, args...] { return g(args...); };
    lm();
}

二次

规模...操作员

大相当大的..。操作员也被归类为包扩展。

二次

代码语言:javascript
复制
template<class... Types>
struct count {
    static const std::size_t value = sizeof...(Types);
};

二次

动态异常规范

类中的异常列表。动态异常规范也可能是包的扩展。

二次

代码语言:javascript
复制
template<class...X> void func(int arg) throw(X...)
{
 // ... throw different Xs in different situations
}

二次

对齐说明符

在类型列表和关键字使用的表达式列表中都允许进行包扩展。对齐...

属性列表

的列表中允许进行包扩展。属性,如[[attributes...]]例如:void [[attributes...]] function()...

Fold-expressions In fold-expressions, the pattern is the entire subexpression that does not contain an unexpanded parameter pack. Using-declarations In using declaration, ellipsis may appear in the list of declarators, this is useful when deriving from a parameter pack: template <typename... bases> struct X : bases... { using bases::g...; }; X<B, D> x; // OK: B::g and D::g introduced

(since C++17)

注记

二次

代码语言:javascript
复制
#include <iostream>
 
void tprintf(const char* format) // base function
{
    std::cout << format;
}
 
template<typename T, typename... Targs>
void tprintf(const char* format, T value, Targs... Fargs) // recursive variadic function
{
    for ( ; *format != '\0'; format++ ) {
        if ( *format == '%' ) {
           std::cout << value;
           tprintf(format+1, Fargs...); // recursive call
           return;
        }
        std::cout << *format;
    }
}
 
int main()
{
    tprintf("% world% %\n","Hello",'!',123);
    return 0;
}

二次

产出:

二次

代码语言:javascript
复制
Hello world! 123

二次

上面的示例定义了一个类似于std::printf,它用值替换格式字符串中字符%的每次出现。

当只传递格式字符串且没有参数展开时,将调用第一个重载。

第二个重载包含参数头和参数包的单独模板参数,这允许递归调用只传递参数的尾部,直到参数变为空。

Targs是模板参数包,并且Fargs函数参数包。

另见

功能模板

*。

类模板

查询参数包中的元素数。

C型变分函数

预处理宏也可以是可变的。

代码语言:txt
复制
 ? cppreference.com

在CreativeCommonsAttribution下授权-ShareAlike未移植许可v3.0。

扫码关注腾讯云开发者

领取腾讯云代金券

http://www.vxiaotou.com