+ 1
how to unpack parameters in variadic templates
----
28 Answers
+ 2
By using a fold expression, you can compute the result of using a binary operator over all arguments of the pack. For
template < typename... Args >
auto foo( Args... args ) {}
"args" represents the unexpanded pack or the first variadic argument, while ... represents the arguments to be expanded. The expression "args" appears in is then expanded for all arguments in the pack. The most important thing to know is in which way the expressions
( ... op args )
( args op ... )
( init op ... op args )
( args op ... op init )
are evaluated for an operator op and an initial value init; the corresponding table can be found in the link I shared earlier.
If the parameter pack is empty, the expression is usually ill-formed, except for the operators &&, || and ,. By using the sizeof...() operator, you can check the size of the parameter pack at compile time.
I wrote some examples of possible use cases for direct visualization:
https://code.sololearn.com/cruo0rNL68bq/?ref=app
+ 1
In many contexts, you can use a fold expression that is automatically expanded by the compiler:
https://en.cppreference.com/w/cpp/language/fold
But it really depends on what you are trying to achieve in the first place.
+ 1
Roughly yes, but maybe "args" is not exactly the current element in the sense of the first argument, but more of a placeholder that every argument in the pack is substituted into when the pack is expanded. For example, we could go ahead and write a print_double() function on top of the print() function from earlier:
template < typename... Args >
auto print_double( const Args&... args )
{
print( args + args... );
}
If you call it in a fashion like this
print_double (
7.5,
std: :string( "Hello" ),
std::complex< float >( 4, 2 )
);
the function call would be expanded to:
print(
7.5 + 7.5,
std::string( "Hello" ) + std::string( "Hello" ),
std::complex< float >( 4, 2 ) + std::complex< float >( 4, 2 )
);
As you can see, each argument of print_double() is substituted into both occurences of "args" upon expansion, and therefore everything is print twice correctly. That's at least how I like to think of it.
+ 1
You can't use ... on its own. It basically tells the compiler to take the expression the unexpanded pack appears in, substitute every pack argument into it, and concatenate the resulting expressions via the given binary operator. In foo4(), Types is used as an argument to std::is_same_v<> on the left side, while ... appears on the right side of &&, so it's not alone or anything.
In the first code you posted, the size of the parameter pack doesn't decrease, so you never reach the base case and end up in an infinite recursive loop.
The second code has no random calculations, it works exactly as expected. This is how it is executed:
First call:
a = 2, args = ( 3, 7 )
-> print a = 2
call print( 3 + 3, 7 + 7 )
Second call:
a = 3 + 3 = 6, args = ( 7 + 7 = 14 )
-> print a = 6
call print( 14 + 14 )
Third call:
a = 14 + 14 = 28, args = ()
-> print a = 28
call print()
Fourth call:
Base case, recursion stops
+ 1
foo4() is going to return true if the decayed types of all arguments match (not their values). Since the parameter pack is passed by value, all types will decay, e.g. string literals decay to const char*, functions decay to function pointers, and so on.
0
recursive method is getting annoying and i cant do everything with it
0
its confusing
0
can u explain them
0
also i dont understand the three dots
0
Google explanations are confusing
0
unary folds are my problem
0
on sample 3 does forwarding pass all arguments?
0
Sure, otherwise the first static assertion in main() would fail.
0
this is magical
0
so arg is the current argument and ... is the others unpacked arguments?
0
in ur code i saw ... is used alone in foo4 function
0
what does it mean
0
run the code
0
it has no end