Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c++

gcc

g++

c++17

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>();
}
like image 967
Bernd Avatar asked Aug 13 '20 07:08

Bernd


People also ask

How to resolve the GCC error “default argument for parameter of type …”?

How to resolve the GCC error message “default argument for parameter of type … has type …” Somewhere in your code you have a function with a default argument like this: void doSomething(int arg1, std::set<std::string> arg2 = 15) {/*...*/}

Why do default parameters not work in managed extension for C++?

To maintain the behavior that you want across programming languages, methods that use default parameters should be replaced with method overloads that provide the default parameters. The compiler ignores the values of default parameters for Managed Extension for C++ when it accesses managed code.

Why can’t I call a function with a default argument?

Maybe one looks suspicious – if not, do the following for each default argument: Check if the type of the function has a constructor that can be called with the given types. If it doesn’t have such a constructor, replace it by a valid default argument.

Is it possible to use default parameters in methods?

This rule has been deprecated. For more information, see Deprecated rules. Methods that use default parameters are allowed under the Common Language Specification (CLS); however, the CLS allows compilers to ignore the values that are assigned to these parameters.


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.

like image 190
Barry Avatar answered Oct 07 '22 15:10

Barry