Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inconsistency in function type decay between variadic/non-variadic templates?

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?

like image 388
Jamboree Avatar asked Aug 31 '16 02:08

Jamboree


1 Answers

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.

like image 167
Columbo Avatar answered Nov 20 '22 04:11

Columbo