Parameter Pack in C++
Template parameter pack and function parameter pack
1. Introduction
A parameter pack can pack multiple parameters into a single parameter by placing an ellipsis to the left of the parameter name. The basic systax of parameter pack can look like this.
1 | template<class... Types> |
Based on the context of parameter pack is used, we have two types of parameter pack, namely, template parameter pack and function parameter pack.
2. Template Parameter Pack
Based on the type declared in template parameter pack. There are 3 kinds of template parameter pack.
- Type parameter pack
- Non-type parameter pack
- Template template parameter pack
We will only talk about the first two types.
2.1 Type parameter pack
This kind of parameter pack captures a sequence of types passed as template arguments. It allows you to work with a variable number(zero or more) of types within a template. You can then expand this pack to generate type-specific code or to pass the types to other templates or functions.
1 | template<class... Types> |
2.2 Non-type parameter pack
This type of parameter pack captures a sequence of non-type template arguments. Non-type parameters can be integral types, pointers, references, or enums. They are used when you need to work with values known at compile time
.
1 | template<int... Values> |
2.3 Position in parameter list
In a primary class template, the template parameter pack must be at the end of the parameter list.
1 | template<typename... U, typename T> |
1 | <source>:9:10: error: parameter pack 'U' must be at the end of the template parameter list |
3. Function Parameter Pack
A function parameter pack is a function parameter that represents any number of function parameters. To use function parameter pack, we often use a template parameter pack as a function parameter. The template parameter pack then is expanded by the function parameter pack.
1 | template<class... Types> |
3.1 trailing function parameter pack and non-trailing function parameter pack
If a function parameter pack is the last function parameter of a function template, then it is a trailing function parameter pack
. Otherwise, it is a non-trailing function parameter pack.
Rule for non-trailing function parameter pack instantiation
A non-trailing function parameter pack can be deduced only from the explicitly specified arguments when the function template is called. If the function template is called without explicit arguments, the non-trailing function parameter pack must be empty.
1 | template<class...A, class...B> void func(A...arg1,int sz1, int sz2, B...arg2) |
4. Parameter pack expansion
4.1 Function argument list
1 | template<class... Us> |
1 | 0x7ffd02d21a0c |
4.2 Parenthesized initializers
Used as a class initializer.
1 | template<typename... T> |
4.3 sizeof…
sizeof...
is an operator in C++ that returns the number of elements in a parameter pack.
1 | template<typename... Args> |
4.4 Braced-enclosed initializers
Used in braced-init-list.
1 | template<typename... Ts> |
4.5 Function parameter list
An ellipsis appears in a parameter declaration, even if the parameter name is being ignored, it is still a valid parameter pack and can be expanded.
1 | template<typename... Ts> |
1 | template<typename T, class... Us> |
4.6 Template parameter list
1 | template<typename... T> |
4.7 Lambda captures
1 | template<class... T> |
4.8 More
More usages can be found at cppreference.com
5. Variadic macro
5.1 __VA_ARGS__
- __VA_ARGS__ is a predefined macro in C++.
- It represents a variable number of arguments passed to a variadic macro.
- It is used within a macro definition to refer to the arguments passed to the macro.
- It allows you to create macros that can accept a variable number of arguments.
1 |
|
The following example is a misuse case of __VA_ARGS__. The reason is that the stream insertion operator << expects its right-hand side to be a single expression, not a list of potentially different types of expressions. As a result, this can lead to compilation errors or unexpected behavior.
1 |
|
5.2 #__VA_ARGS__
#__VA_ARGS__ is a stringizing operator in C++.
When used within a macro definition, #__VA_ARGS__ converts the arguments passed to the macro into a string literal.
It is typically used to stringify the arguments for debugging or logging purposes.
1 |
|
6. Practice
1 |
|
- Recursive construction builds the Tuple structure from the provided arguments
- First call,
Tuple<int, double, const char*>
- First call,
- Inside the constructor of
Tuple<int, double, const char*>
, value is initialized with 1. The constructor of rest (of typeTuple<double, const char*>
) is called recursively with the remaining arguments 2.3 and “hello”.
- Inside the constructor of
- Second call, inside the constructor of
Tuple<double, const char*>
, value is initialized with 2.3. The constructor of rest (of typeTuple<const char*>
) is called recursively with the remaining argument “hello”.
- Second call, inside the constructor of
- Third call, inside the constructor of
Tuple<const char*>
, value is initialized with “hello”. Since there are no more arguments, the constructor of rest (of typeTuple<>
) is called.
- Third call, inside the constructor of
- Since
Tuple<>
is the primary template without any arguments, its constructor does nothing. It serves as the base case of the recursion.
- Since
- Recursive get() function
- The expression
rest.template get<index - 1>()
is calling the get() function on the memberrest
of the Tuple. template
is used to specify thatget
is a template member function of therest
object. This disambiguates the use of theget()
member function.
- The expression
References
Parameter Pack in C++