Consider the following snippet (available on compiler epxlorer):
template<typename T, typename... Args>
auto foo(Args&&... args) {}
template<typename... Args>
auto foo(Args&&... args) {}
int main() {
foo<char>('a');
}
It compiles perfectly fine for GCC and fails for both Clang and MSVC (with compiler saying ambiguous call)
Why do Clang and MSVC fail to such seemingly obvious deduction?
EDIT: GCC provides me with the expected solution as a user, is there an easy way to push clang and msvc to choose the template without much change of the original code?
If you examine the additional diagnostic lines from the compiler, you'll see that it says
<source>(6): note: could be 'auto foo<char>(char &&)'
<source>(3): note: or 'auto foo<char,char>(char &&)'
(from MSVC; Clang is similar)
In this case, since the first (only) parameter to the function foo
is a char
, the compiler cannot distinguish between the one template parameter and two template parameter versions of the template.
If you change your function call to
foo<char>(10);
it will compile.
There is an example in the language spec ("Partial ordering of function templates", [temp.func.order]
) very similar to your code:
template<class T, class... U> void f(T, U...); // #1
template<class T > void f(T); // #2
void h(int i) {
f(&i); // error: ambiguous
}
Since GCC compiles it, this is a bug in GCC.
After some tests, and using the mentioned reference to the standard: [temp.func.order], [temp.deduct.partial], I came to the following understanding of the situation.
Considering the example given in the question:
template<typename T, typename... Args> auto foo(Args&&... args) {} //#1
template<typename... Args> auto foo(Args&&... args) {} //#2
#2 is a function with a variadic parameter pack that can be deduced. can be deduced, not have to. Thus, nothing prevents the user to explicitly specify the template arguments.
Therefore, foo<char>('a')
can be as much an explicit instantiation of #2 as an instantiation of #1, provoking the ambiguity. The standard does not favor a preferred match between the overload #1 and #2.
GCC went beyond the standard within its implementation by attributing a higher preference for #1 when a template argument is manually given, while Clang and MSVC kept it vanilla.
Furthermore, ambiguity appears only when the first arguments from the variadic pack and T resolve to the exact same type.
Here are the solutions that I found for my use case. (Forward object construction or a variadic pack of objects)
Declare an extra function specializing for one argument, this would take precedence over the variadic-based ones. (Does not scale or generalize)
template<typename T> auto foo(T&& args) {}
//or
template<typename T, typename Arg> auto foo(Arg&& arg) {}
Disable the overload when the first argument of the non-empty parameter pack is same as the given type T.
template<typename T, typename... Args>
constexpr bool is_valid() {
if constexpr(sizeof...(Args)==0)
return true;
else
return !std::is_same_v<T,std::tuple_element_t<0,std::tuple<Args...> > > ;
}
template<typename T, typename... Args, typename = std::enable_if_t<is_valid<T,Args...>()> >
auto foo(Args&&... args) {}
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