c++language-lawyerfunction-templatesparameter-packpack-expansion

MSVC vs Clang/GCC bug during overload resolution of function templates one of which contains a parameter pack


I was using parameter pack when I noticed that one such case(shown below) compiles fine in gcc and clang but not in msvc:

template<class T> void func(T a, T b= T{})
{
    
}
template<class T, class... S> void func(T a, S... b)
{

}
 
int main()
{
    func(1); // Should this call succeed? 
}

Here is the link for verifying the same: https://godbolt.org/z/8KsrcnMez

As can be seen the above program fails in msvc with the error message:

<source>(13): error C2668: 'func': ambiguous call to overloaded function
<source>(6): note: could be 'void func<int,>(T)'
        with
        [
            T=int
        ]
<source>(2): note: or       'void func<int>(T,T)'
        with
        [
            T=int
        ]
<source>(13): note: while trying to match the argument list '(int)'

But the same compiles fine with gcc and clang.

Which compiler(s) is right here?


Solution

  • MSVC is right in rejecting the code. According to temp.func.order#5.example-2 the call func(1) is ambiguous. The example given in the standard is as follows:

    Note: Since, in a call context, such type deduction considers only parameters for which there are explicit call arguments, some parameters are ignored (namely, function parameter packs, parameters with default arguments, and ellipsis parameters).

    template<class T         > void g(T, T = T());          // #3
    template<class T, class... U> void g(T, U ...);         // #4
    
    void h() {
     g(42);                                                // error: ambiguous
    }
    

    This means that in the example in question, the call func(1) is ambiguous as well.

    Errata

    Note that cppreference has errata in below given example:

    template<class T>
    void g(T, T = T()); // #1
    template<class T, class... U>
    void g(T, U...);    // #2
    
    void h()
    {
       g(42); // calls #1 due to the tie-breaker between parameter pack and omitted parameter
    }
    

    As can be seen in the above example, cpprefernce incorrectly says that the first overload #1 should be choosen.

    This has been fixed in recent edit that i made.