博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

模版完整: 参数包

Posted on 2018-04-27 10:41  bw_0927  阅读(718)  评论(0)    收藏  举报

https://www.cnblogs.com/my_life/articles/8950031.html

https://blog.csdn.net/zhouguoqionghai/article/details/48661523

http://en.cppreference.com/w/cpp/language/parameter_pack

http://www.cnblogs.com/my_life/articles/8961332.html     介绍了类型、非类型模版参数

http://en.cppreference.com/w/cpp/language/parameter_pack

http://zh.cppreference.com/w/cpp/language/parameter_pack

http://www.cnblogs.com/liyiwen/archive/2013/04/13/3018608.html    解包

 

 

 

 

template <class... T>
void f(T... args);
上面的参数args前面有省略号,所以它就是一个可变模版参数,我们把带省略号的参数称为“参数包”,它里面包含了0到N(N>=0)个模版参数。

 

/* typename... Args定义可变模板参数 */

/* Args... args展开模板参数 定义函数形参 */
/* args...展开函数实参 */
template<typename Function, typename... Args> 
void call(Function fun, Args... args)
{
    fun(args...);
}

 

 

 

在C++11之前, 类模板和函数模板只能含有固定数量的模板参数, 在C++11之后的新特性可变模板参数允许包含0到任意个模板参数. 声明可变模板参数时需要在typename和class后面带上省略号'...'.

 

一个可变参数类模板定义如下:

template<typename ... Types>      //这是类型模版参数,模版参数必须是类型
class Tuple
{};

可以用任意数量的类型来实例化Tuple:

Tuple<> t0;
Tuple<int> t1;
Tuple<int, string> t2;
// Tuple<0> error;  0 is not a type    //template<typename ... Types> ,这是类型模版参数,模版参数必须是类型,0是非类型

如果想避免出现用0个模板参数来实例化可变参数模板,可以这样定义模板:

template<typename T, typename ... Types>
class Tuple
{};

此时在实例化时,必须传入至少一个模板参数,否则无法编译。
同样地,可以定义接收任意参数的可变参数函数模板:

template<typename ... Types>
void f(Types ... args);

// 一些合法的调用
f();
f(1);
f(3.4, "hello");

 

====================================================================== 

模版参数包也是分非类型参数包类型参数包两种情况的:

  •  

type ... name(optional)    (3)  (since C++11)             //非类型模版参数;  是固定的int,string等固定的类型

 A non-type template parameter pack with an optional name.    (非类型参数包)  类型固定,参数不固定

例如: template <int N, int... Rest>   暂没找到好的例子

 

typename|class ... name(optional)        (since C++11)      //类型模版参数;

A type template parameter pack with an optional name.       (类型参数包),类型不固定

 

A template parameter pack is a template parameter that accepts zero or more template arguments (non-types, types, or templates).

模版参数包本质上就是一个模版参数,只不过该模版参数可以接收0个或者多个模版实参(例如非类型,类型,模版)

 

A function parameter pack is a function parameter that accepts zero or more function arguments.

A template with at least one parameter pack is called a variadic template( 可变参数模板 ; 可变模板 ; 变长参数模板 ; 变参模板).

 

 

Explanation

A variadic class template can be instantiated with any number of template arguments:       可变类模版

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

A variadic function template can be called with any number of function arguments (the template arguments are deduced through template argument deduction):           可变函数模版

template<class ... Types> void f(Types ... args);        类型参数包
f();       // OK: args contains no arguments
f(1);      // OK: args contains one argument: int,   自动推倒出来的,无需像类模版那样显示指定类型,因为模板函数的参数可以从其传入参数中解析出来。  也可以写成f<int>(1)
http://www.cnblogs.com/my_life/articles/5165974.html

f(2, 1.0); // OK: args contains two arguments: int and double

In a primary class template, the template parameter pack must be the final parameter in the template parameter list.  类模版中,可变参数包必须放最后

In a function template, the template parameter pack may appear earlier in the list provided that all following parameters can be deduced from the function arguments, or have default arguments:

函数模版中,可变参数包可以放前面,只有其后面的模版参数可以推倒出来,或者有默认值

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}

 

Pack expansion语法

A pattern followed by an ellipsis, in which the name of at least one parameter pack appears at least once, is expanded into zero or more comma-separated instantiations of the pattern, where the name of the parameter pack is replaced by each of the elements from the pack, in order.

template<class ... Us> void f(Us... pargs) {}
template<class ...Ts> void g(Ts... args) { //class ...Ts 是可变模版非类型参数; f(&args...); // “args...” is a pack expansion; args...是包展开; &再取他们的地址 // “&args” is its pattern; &args是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

If the names of two parameter packs appear in the same pattern, they are expanded simultaneously, and they must have the same length:

 

 

Expansion loci  参数展开的场景如下:

 

Depending on where the expansion takes place, the resulting comma-separated list is a different kind of list: function parameter list, member initializer list, attribute list, etc. The following is the list of all allowed contexts

  • Function parameter list 函数形参

 

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>           //typename ...Ts 模版非类型可变参数包,  int... N模版类型可变参数包; 可变的Ts和N的元素是一一对应的;  T1[N1], T2[N2], T3[N3]....
void g(Ts (&...arr)[N]) {
}
int n[1]; g<const char, int>("a", n); // Ts (&...arr)[N] expands to // const char (&)[2], int(&)[1]

 

https://www.cnblogs.com/my_life/articles/4076180.html

数组的指针:  int (*arr)[4];

数组的引用:  int (&arr)[4];

指向数组的指针:   int* arr_p[4];

 

Note: In the pattern Ts (&...arr)[N], the ellipsis is the innermost element, not the last element as in all other pack expansions.

Note: Ts (&...)[N] is not allowed because the C++11 grammar requires the parenthesized ellipsis to have a nameCWG #1488.  

Ts (&...)[N] 这种格式在C++11中不运行,需要一个name, Ts (&...arr_name)[N] 

  • Function argument lists   函数实参

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)
  • Parenthesized initializers  圆括号的初始化

Class c1(&args...);             // calls Class::Class(&E1, &E2, &E3)
Class c2 = Class(n, ++args...); // calls Class::Class(n, ++E1, ++E2, ++E3);
::new((void *)p) U(std::forward<Args>(args)...) // std::allocator::allocate
  • Brace-enclosed initializers  花括号的初始化列表

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)... };       带逗号表达式的花括号初始化列表
}
https://www.cnblogs.com/my_life/articles/8950031.html

我们知道逗号表达式会按顺序执行逗号前面的表达式,比如:


d = (a = b, c); 

这个表达式会按顺序执行:b会先赋值给a,接着括号中的逗号表达式返回c的值,因此d将等于c。

 
  • Template argument lists  模版实参

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> 
}
  • Template parameter list 模版形参

    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]>
    };
  • Base specifiers and member initializer lists  基类指定符与成员初始化列表

template<class... Mixins>
class X : public Mixins... {
 public:
    X(const Mixins&... mixins) : Mixins(mixins)... { }
};


  • Lambda captures  Lambda 捕获

template<class ...Args>
void f(Args... args) {
    auto lm = [&, args...] { return g(args...); };
    lm();
}



 

  • The sizeof... operator       sizeof... 运算符

 

    The sizeof... operator is classified as a pack expansion as well

 

template<class... Types>
struct count {
    static const std::size_t value = sizeof...(Types);
};

 

 

  • Dynamic exception specifications  动态异常规定中的异常列表亦可为包展开

  • Alignment specifier    对齐指定符

  • Attribute list  属性列表

  • Fold-expressions  折叠表达式

  • Using-declarations   using 声明

在 using 声明中,省略号可以出现于声明器列表内,这对于从参数包导出有用:

template <typename... bases>
struct X : bases... {
	using bases::g...;
};
X<B, D> x; // OK :引入 B::g 与 D::g