Consider the following contrived piece of code:
template <class... > struct pack { };
template <class R, class T, class... Args>
int foo(pack<T>, Args...)
{
return sizeof(R);
}
template <class R, class T, class... Ts, class... Args>
int foo(pack<T, Ts...>, Args... args)
{
return foo<T>(pack<Ts...>{}, args...);
}
int main() {
// gcc: OK, clang: ambiguous
foo<int>(pack<int>{});
// gcc: ambiguous, clang: ambiguous
foo<int>(pack<int>{}, 0);
}
Both gcc and clang accept both calls if the 2nd overload is changed to take a pack of at least 2 types instead of a pack of at least one type:
template <class R, class T, class T2, class... Ts, class... Args>
int foo(pack<T, T2, Ts...>, Args... args)
{
return foo<T>(pack<T2, Ts...>{}, args...);
}
If the non-deduced template parameter is moved to a deduced template parameter, then:
template <class... > struct pack { };
template <class R, class T, class... Args>
int foo(pack<R>, pack<T>, Args...)
{
return sizeof(R);
}
template <class R, class T, class... Ts, class... Args>
int foo(pack<R>, pack<T, Ts...>, Args... args)
{
return foo(pack<T>{}, pack<Ts...>{}, args...);
}
int main() {
// gcc ok with both, clang rejects both as ambiguous
foo(pack<int>{}, pack<int>{});
foo(pack<int>{}, pack<int>{}, 0);
}
I'd expect all the calls to be OK in every version of this. What is the expected outcome of the above code examples?
In Function overloading, sometimes a situation can occur when the compiler is unable to choose between two correctly overloaded functions. This situation is said to be ambiguous. Ambiguous statements are error-generating statements and the programs containing ambiguity will not compile. Automatic type conversions are the main cause of ambiguity.
The ordering is partial because there can be some templates that are considered equally specialized. The compiler chooses the most specialized template function available from the possible matches.
prog.cpp:25:12: error: call of overloaded ‘test (float)’ is ambiguous The above code will throw an error because the test (2.5f) function call will look for float function if not present it is only promoted to double, but there is no function definition with double or float type of parameter.
………. …… ….. ……. where, T is template argument accepting different arguments and class is a keyword. The name of the function templates are the same but called with different arguments is known as function template overloading.
I believe now that clang is correct to reject and gcc is incorrect to accept those forms that it does. Here's a simplified example:
template <class...> struct pack { };
// (1)
template <class T>
void foo(pack<T> ) { }
// (2)
template <class T, class... Ts>
void foo(pack<T, Ts...> ) { }
int main() {
foo(pack<int>{});
}
Both overloads are valid, and deducing (2) from (1) succeeds straightforwardly. The only issue is can we deduce (1) from (2). I'd initially think no... But [temp.deduct.type]/9 states:
If
P
has a form that contains<T>
or<i>
, then each argument Pi of the respective template argument list ofP
is compared with the corresponding argument Ai of the corresponding template argument list ofA
. [...] During partial ordering (14.8.2.4), if Ai was originally a pack expansion:
— ifP
does not contain a template argument corresponding to Ai then Ai is ignored;
So when we synthesize types for <T, Ts...>
(say <U, Xs...>
), we deduce T=U
and then there is no template argument corresponding to the pack expension Xs...
, so we ignore it. All the non-ignored template parameters succeeded in template deduction, so we consider deducing (1) from (2) to be success.
Since deduction succeeds in both directions, neither function template is considered more specialized than the other, and the call should be ambiguous.
I have not yet submitted a bug report, waiting on some confirmation from the community.
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