Using constrained template base member function in derived class, which one should be hidden?

Viewed 90

Let's consider the code bellow where base members are declared in the derived class. GCC and Clang do not agree on which member is going to be hidden:

template <class T>
concept C = true;

struct base
    {
    template <class T>
    void foo0 (T&&);

    template <class T>
    void foo1(T&&) requires C<T>;

    template <C T>
    void foo2(T&&);

    void foo3(C auto &&);

    template <C T, class U>
    void foo4(T&&, U&&);

    template <C T, class U>
    void foo5(T&&, U&&);
    };

struct derived
    : base
    {
    using base::foo0;
    using base::foo1;
    using base::foo2;
    using base::foo3;
    using base::foo4;
    using base::foo5;

    template <class T>
    void foo0(T&&);

    template <class T>
    void foo1(T&&);

    template <class T>
    void foo2(T&&);

    void foo3(auto &&);

    template <class T, class U>
    void foo4(T&&, U&&);

    template <class T, class U>  //inversed template parmeter order
    void foo5(U&&, T&&);         
    };

When derived member function are called, GCC and Clang does not always agree on which function should be called: (compiler explorer link)

void g(derived d, int i) //                              Description of the difference
    {//Function called       |  by GCC   |  by Clang   | between base and derived member
                         //  -----------------------------------------              
    d. foo0(i);          //  |  derived  |  derived    | same signature
    d. foo1(i);          //  |  base     |  base       | trailing require clause
    d. foo2(i);          //  |  base     |  derived    | constrained template parameter
    d. foo3(i);          //  |  base     |  derived    | constrained function parameter
    d. foo4(i,i);        //  |  base     |  derived    | constrained template parameter
    d. foo5(i,i);        //  |  base     |  base       | reversed template parameter order
                                                            // with constraint.
    }

Morally, I don't see way foo1 foo2 and foo3 should be different cases.

Which compiler is right?

The paragraph of the standard that specifies whither a base member declaration is hidden or not is [namespace.udecl]/14:

When a using-declarator brings declarations from a base class into a derived class, member functions and member function templates in the derived class override and/or hide member functions and member function templates with the same name, parameter-type-list ([dcl.fct]), trailing requires-clause (if any), cv-qualification, and ref-qualifier (if any), in a base class (rather than conflicting). Such hidden or overridden declarations are excluded from the set of declarations introduced by the using-declarator.

After a first reading, it looks like Clang is applying the standard at the letter. But...

But what means the letter here? What is suppose to mean equivalent parameter-type-list for a template without its template-head? Consider the case of foo5, it is clear Clang considers the template-head, at least the template parameter order.

0 Answers
Related