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.