GCC can not resolve method call with defaulted parameter and following parameter pack

Viewed 167

Is GCC allowed to reject the following code due to ambiguity? To me it looks like a bug. It compiles fine with msvc, clang and icc.

See here: https://godbolt.org/z/9fsnhx

#include <iostream>

class A
{
public:
    template<typename T>
    void Foo(int={}){
        std::cout << "A";
    }

    template<
        typename... T
        ,typename... Args
    >
    void Foo(int={}, Args&&... args)
    {    
        std::cout << "B";
    }
};

int main()
{
    A a;
    a.Foo<int>();
}
1 Answers

I think this is a gcc bug. As Oliv notes in the comments, if you provide an argument for the defaulted parameter, gcc accepts - but this should be irrelevant in this case.

The relevant rules to point out here are [temp.deduct.partial], paragraph 3:

The types used to determine the ordering depend on the context in which the partial ordering is done:

  • In the context of a function call, the types used are those function parameter types for which the function call has arguments.138

(the footnote says):

Default arguments are not considered to be arguments in this context; they only become arguments after a function has been selected.

Paragraph 11:

If, after considering the above, function template F is at least as specialized as function template G and vice-versa, and if G has a trailing function parameter pack for which F does not have a corresponding parameter, and if F does not have a trailing function parameter pack, then F is more specialized than G.

Paragraph 12:

In most cases, deduction fails if not all template parameters have values, but for partial ordering purposes a template parameter may remain without a value provided it is not used in the types being used for partial ordering. [ Note: A template parameter used in a non-deduced context is considered used. — end note ] [ Example:

template <class T> T f(int);            // #1
template <class T, class U> T f(U);     // #2
void g() {
  f<int>(1);                            // calls #1
}

— end example ]

In short, when considering partial ordering here:

  1. The default argument doesn't matter, we just consider the two function templates taking an int as their first parameter.

  2. The T in the first overload and T... in the second overload also don't matter. They're not in the set of types used for partial ordering, they're not arguments to the function. This is similar to the example in paragraph 12, where the template parameter also named T also does not play a role.

So basically we're ordering between:

void Foo(int);
template <typename... Args> void Foo(int, Args&&...);

which is a very straightforward case where the first is more specialized. gcc gets this wrong - I submitted 96602.

Related