The following code compiles and runs ok.
void foo() {
}
template <typename T, typename... Args>
void foo(T x, Args... args) {
cout << x << endl;
foo(args...);
}
// inside main()
foo(1,1,1);
This other code does not compile:
void foo() {
}
template <typename... Args, typename T>
void foo(Args... args, T x) {
foo(args...);
cout << x << endl;
}
// inside main()
foo(1,1,1);
The compiler says that there is no matching function for call to foo(1,1,1)
and says that foo(Args... args, T x)
is a candidate, but template argument deduction/substitution failed, because candidate expects 1 argument, but 3 were provided.
Is there any ambiguity with this situation that no compiler can handle? This compile error just seems illogical to me. Maybe this is not in accordance, on purpose, with the C++ standard?
Parameter packs can only be expanded in a strictly-defined list of contexts, and operator , is not one of them. In other words, it's not possible to use pack expansion to generate an expression consisting of a series of subexpressions delimited by operator , .
Parameter packs (C++11) A parameter pack can be a type of parameter for templates. Unlike previous parameters, which can only bind to a single argument, a parameter pack can pack multiple parameters into a single parameter by placing an ellipsis to the left of the parameter name.
Variadic templates are class or function templates, that can take any variable(zero or more) number of arguments. In C++, templates can have a fixed number of parameters only that have to be specified at the time of declaration. However, variadic templates help to overcome this issue.
A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.
(This answer is based on @JohannesSchaub-litb's comments)
According to the standard, template parameter pack is not deducible if it is used in a function parameter pack not at the end of the parameter list.
§14.8.2.1/1 Deducing template arguments from a function call [temp.deduct.call]:
When a function parameter pack appears in a non-deduced context ([temp.deduct.type]), the type of that parameter pack is never deduced. [ Example:
template<class T1, class ... Types> void g1(Types ..., T1); void h(int x, float& y) { const int z = x; g1(x, y, z); // error: Types is not deduced g1<int, int, int>(x, y, z); // OK, no deduction occurs }
— end example ]
And about non-deduced context, §14.8.2.5/5 Deducing template arguments from a type [temp.deduct.type]:
A function parameter pack that does not occur at the end of the parameter-declaration-list.
So the direct reason of foo(1,1,1);
failed is that the template parameter Args
is not deduced, which is necessary to make the function invocation valid.
To explain the error message, a template parameter pack not deduced will be deduced to an empty sequence of template arguments[1], it means it'll be omitted. Then foo(1,1,1);
failed because the number of arguments doesn't match, that's what compiler complained.
Just as the example from standard shown, you could specify the template argument explicitly to avoid type deduction, even though it doesn't meet the original intent of your code. Such as:
template <typename T, typename... Args>
void foo(Args... args, T x) {
}
int main() {
// inside main()
foo<int, int, int>(1, 1, 1);
}
Here're some additional informations.
[1] I can't find direct expression about this in the standard. The most close one is this, "A trailing template parameter pack ([temp.variadic]) not otherwise deduced will be deduced to an empty sequence of template arguments."
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With