Look at this code (godbolt):
template <typename ...T>
void func(const char *it, T &&...t) {}
template <typename A, typename B>
void func(A &&a, B &&b) {}
int main() {
func("a", 0);
}
Clang doesn't compile this code (because the func
call is ambiguous), while gcc compiles it. If I remove the variadic parameter, and use a simple typename T
, then clang compiles the code.
Which compiler is correct? If clang is correct then please explain why there is a difference between the variadic and non-variadic case regarding overload resolution.
Do template parameter packs affect overload resolution?
Generally yes, but it wouldn't matter in your example.
[temp.deduct.partial] describes the specifics.
For example, (T)
would be more specialized than (T...)
.
In your example
template <typename ...T> void func(const char *it, T &&...t) {} template <typename A, typename B> void func(A &&a, B &&b) {}
... with two call arguments "a", 0
, the compiler would attempt to synthesize types for T...
, A
, and B
, and see if deduction would succeed if the other function was called with arguments of that type.
const char*
can't generally accept any hypothetical type A
, and a single B
can't generally accept a pack of hypothetical T
s, so deduction fails both ways.
Therefore, neither template is more specialized and the call is ambiguous.
GCC is wrong to prefer the second overload, where A = const char(&)[2]
, as can be seen in the assembly output:
call void func<char const (&) [2], int>(char const (&) [2], int&&)
This appears to be GCC bug 41958
In other words, Clang is correct, and GCC has a bug.
Note 1: There is a related Clang issue 47604, as @cigien has pointed out, but this doesn't seem to be relevant here and only manifests itself when producing a function pointer from an overloaded function, and the rules there are slightly different because there are no call arguments.
Note 2: Also, [over.ics.scs] states that array-to-pointer conversions (such as from ""
to const char*
) is an Exact Match conversion, so it's not any worse than A&&
binding to const char(&)[1]
.