Look at this code snippet
template<class T>
void print(T var)
{
std::cout << var << " ";
}
template<class... Args>
void Variadic(Args... args)
{
print(args...);
}
int main()
{
Variadic();
}
When I compile it says:
candidate: template void print(T)
candidate expects 1 argument, 0 provided
And he's right. In fact, I didn't provide any argument in the Parameter Pack.
But why, then, this code compiles?
template<class T>
void print(T var)
{
std::cout << var << " ";
}
template<class... Args>
void Variadic(Args... args)
{
auto x = {0, (print(args), 0)...};
}
int main()
{
Variadic();
}
The first thing I do is to push the first 0 into the initializer_list<>
Ok, now let’s move on: the compiler sees
(print(args), 0)...
It tries to call print()… oh wait… the Parameter Pack is empty and the print() function takes 1 parameter.
Why does it evaluate to auto x = {0};
, then?
Why doesn't the compiler give me the exact same error as before?
You misunderstand how the ...
expansion operator works. In your example, when args
is an empty pack, (print(args), 0)...
expands to nothing, not print()
.
If args
was given as x
it would expand to print(x), 0
.
If args
was given as x, y
it would expand to (print(x), 0), (print(y), 0)
.
etc.
Basically it expands all of the expression that contains args
and it is applied to, not just the args
bit itself.
From the standard [temp.variadic]:
- A pack expansion consists of a pattern and an ellipsis, the instantiation of which produces zero or more instantiations of the pattern in a list. The form of the pattern depends on the context in which the expansion occurs.
...
- The instantiation of a pack expansion that is neither a sizeof... expression nor a fold-expression produces a list E1, E2, ..., EN , where N is the number of elements in the pack expansion parameters. Each Ei is generated by instantiating the pattern and replacing each pack expansion parameter with its ith element.