Given a non-variadic function template:
template<class T>
void f(void(t)(T));
And some plain functions:
void f1(int);
void f2(char);
This works:
f(f1);
The type of t
becomes void (*)(int)
.
However, the variadic counterpart:
template<class... T>
void f(void(...t)(T));
// call
f(f1, f2);
does not work. The compilers (gcc & clang) complain about mismatched types void(T)
and void (*)(int)
. See DEMO.
Note that if *
is added explicitly, it works as it should:
template<class... T>
void f(void(*...t)(T));
So, why the non-variadic one can decay the function type while the variadic one cannot?
AFAICS, the code is fine (also supported by the fact that both VC++ and ICC compile it). After all, template argument deduction seems to work with function types just as it does with function pointer or reference types; [temp.deduct.type]/18:
A template-argument can be deduced from a function […] type.
[temp.deduct.call]/1:
For a function parameter pack that occurs at the end of the parameter-declaration-list, deduction is performed for each remaining argument of the call, taking the type
P
of the declarator-id of the function parameter pack as the corresponding function template parameter type. Each deduction deduces template arguments for subsequent positions in the template parameter packs expanded by the function parameter pack.
In particular the latter paragraph confirms that there is some inconcistency, since the (unsuccessful) deduction of the pack T
in the second case reduces to the (successful) deduction in case 1.
My guess is that Clang and GCC decay the parameter types for function templates right at declaration time, but refuse to do so when the parameter is a pack expansion (and then fail to deduce). Clang's error message when we alter the sample call to f(f1)
is
note: candidate template ignored: could not match
'void (T)'
against'void (*)(int)'
So the argument is in fact decayed before deduction.
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