Before c++11, class templates and function templates can contain only a fixed number of template parameters, and c++11 adds variable template parameter properties: allows 0 to any template parameter in the template definition. When declaring a variadic template, you need to add the ellipsis "..." after TypeName or class.
The ellipsis has two effects:
1. Declare a parameter package, which can contain 0 to any template parameter
2. On the right side of the template definition, you can expand the parameter pack to a single parameter
1. Variable parameter template function
The variable parameter template function is defined as follows:
Template<class ... T>void f (T ... args) { cout << sizeof ... (args) << Endl; sizeof ... (args) Gets the number of variable arguments}f (); F (1, 2); F (1, 2.3, "Hello");
A parameter pack can contain 0 or more parameters, and if you need parameters in a parameter pack, you must expand the parameter pack. There are two ways to expand the parameter package: (1) to expand the parameter package through a recursive template function, (2) to expand the parameter package by means of a comma expression and an initialization list.
Expand the Parameter pack
(1) Recursive function expansion parameter package
You need to provide a parameter package expansion function and a recursive termination function with the same name. The parameters of the recursive termination function can be 0,1,2 or multiple (typically 0 or 1), and when the number of arguments remaining in the parameter pack equals the number of arguments to the recursive termination function, the function terminates.
#include <iostream>using namespace std;//recursive termination function void print () { cout << "Empty" << Endl;} Expand function Template<class T, class ... Args>void Print (T head, Args ... rest) { cout << "parameter =" << head << Endl; Print (rest ...); int main () { print (1,2,3,4); return 0;} When print (1,2,3,4) is called, print (2,3,4), print (3,4), print (4) are called successively, and the final call to print () terminates. If the recursive termination function is template<typename t>void print (T a) { cout << a << Endl;} The function call to print (4) terminates.
It is also possible to define functions with the same name but with different parameters by type_traits, implement recursive termination and expansion functions separately, and expand the parameter package
Equivalent to the recursive termination function Template<typename I = 0, typename tuple>typename std::enable_if<i==std::tuple_size<tuple>: : Value>::type printtp (Tuple t) { }//equals expansion function Template<typename I = 0, typename Tuple>typename std::enable_if <i < Std::tuple_size<tuple>::value>::type printtp (tuple t) { std::cout << std::get<i> ( t) << Std::endl; Print out the printtp<i+1> (t) in the tuple;} Template<typename ... Args>void print (args ... args) { printtp (std::make_tuple (args ...);}
(2) initialize list mode expand parameter Pack
The recursive function expands the parameter package and requires a terminating function with the same name to terminate the recursion, and the initialization list can be used to avoid multiple definitions of a terminating function with the same name.
Template<typename t>void Printarg (T a) { cout << a << Endl;} Template<class ... Args>void expand (args ... args) { int arr[] = {(Printarg (args), 0) ...}; or improved for std::initializer_list<int>{(Printarg (args), 0) ...};} {(Printarg (args), 0) ...} will be expanded to {(Printarg (arg1), 0), (Printarg (ARG2), 0), (Printarg (ARG3), 0)}expand ("Hello");
This way of expanding the parameter package, do not need to pass the recursive termination function, but directly in the expand function in the body expansion, Printarg is not a recursive termination function, just a function to handle each parameter in the parameter package. The key to this approach is the comma expression.
The comma expression executes the preceding expression sequentially, such as D = (A = b, c), B is assigned to a, and the comma expression in parentheses returns the value of C, so D is assigned C
c++11 using the list initialization method to initialize an array of edge lengths, you can use int arr[] = {args ...}; Where args is a variable-length set of parameters.
Template<typename ... Args>void print (args ... args) { int arr[] = {(args, 0) ...};//named Args ... (or anonymous ...) Represents all mutable parameter sets that can be args and ... Separate, at this point the args says ... In each parameter. }
You can also use lambda expressions to improve the list initialization described above:
Template<typename ... Args>void expand (args ... args) { std::initializer_list<int>{([&]{cout << args << Endl;} (), 0) ...};}
2. Variable parameter template class
A tuple is a mutable parameter template class:
Template<class ... Types> class tuple; This variable-parameter template class can carry any number of template parameters of any type std::tuple<int> TP1 = std::make_tuple (1); Std::tuple<int, double> TP2 = Std::make_tuple (1,2.4); std::tuple<> TP;
parameter expansion for variable parameter template classes
(1) template recursion and special mode expansion parameter package
The expansion of a Variadic template class typically requires two or three classes, including a class declaration and a template class for a specific specification. A basic mutable parameter template class is defined as follows:
Template<typename ... Args> //forward declaration struct Sum;template<typename first, TypeName ... Rest> //class definition struct Sum<first, rest...>{ enum {value = Sum<first>::value + sum<rest...>:: value};}; Template<typename last>//recursive termination class, template parameters are not necessarily 1, may be 0 or 2 struct sum<last>{ enum{value = sizeof (last)}; The function of this sum class is to compute a size sum of the parameter types in the parameter package at compile time. Or can be modified by std::integral_constant: Template<typename ... Args> //Pre-declaration struct Sum;template<typename first, TypeName ... Rest> //recursive definition of struct sum<first, rest...>: Std::integral_constant<int, Std::integral_constant<int, sizeof (first) >::value + sum<rest...>::value{};template<typename last>//recursive terminating struct SUM<LAST>: Std::integral_constant<int, sizeof (last) >{};
(2) Inheritance mode expansion parameter package
Definition of integer sequence template<int ... >struct indexseq{};//inherit mode, start expand parameter Package template<int N, int ... Indexes>struct makeindexes:makeindexes<n-1, N-1, indexes...>{};//template Special, terminate the conditions of the expansion parameter pack template<int ... Indexes>struct makeindexes<0, indexes...>{ typedef indexseq<indexes...> type;}; int main () { using T = makeindexes<3>::type; The output is struct indexseq<0,1,2> cout << typeid (T). Name () << Endl; Return 0;};/ /makeindexes can be implemented by using if it is not generated by inheritance recursively. Template<int N, int ... Indexes>struct makeindexes{ Using type = makeindexes<n-1,n-1,indexes>::type;}; Template<int ... Indexes>struct makeindexes<0, ... indexes>{ Using type = indexseq<indexes...>;};
You can use the indexseq above to expand and print variable template parameters, such as: Template<int ... Indexes, TypeName ... Args>void Print_helper (indexseq<indexes...>, std::tuple<args...>&& tup) { print (std:: Get<indexes> (Tup) ...);} Template<typename ... Args>void print (args ... args) { print_helper (typename makeindexes<sizeof ... (args) >::type (), std::make_tuple (args ...));
c++11--Variable parameter templates