I stumbled upon this weird name lookup issue, where a base class member function doesn't seem to participate in the overload selection at all, even though it is imported with a using
statement. The member functions of the base and derived classes are both SFINAE'd with enable_if_t
.
I was able to reproduce my issue with the following code: https://gcc.godbolt.org/z/ueQ-kY
#include <iostream>
#include <type_traits>
class MyTag {};
struct Base
{
template <typename RType>
std::enable_if_t<std::is_convertible<RType, int>::value> create(RType /*&&*/ ref)
{
std::cout << "Base::create(RType ref)" << std::endl;
}
};
struct Derived : Base
{
using Base::create;
template <typename Tag>
std::enable_if_t<std::is_same<Tag, MyTag>::value> create(Tag /*&&*/ tag)
{
std::cout << "Derived::create(Tag tag)" << std::endl;
}
};
int main()
{
Derived d;
d.create(MyTag());
d.create(0); // [x86-64 clang 7.0.0 #1] error: no matching member function for call to 'create'
}
While GCC compiles the above code without warnings, clang, icc, and MSVC aren't able to find a suitable overload for the call of d.create(0);
and error the build. In fact, judging from the error messages, it seems like Base::create
isn't even taking part in the overload resolution.
However, when one of the two member function takes its argument as a forwarding reference, the code compiles fine on all major compilers.
Gcc is wrong and should reject your example.
using-declaration: using using-declarator-list ;
[namespace.udecl]/1
Each using-declarator in a using-declaration introduces a set of declarations into the declarative region in which the using-declaration appears. The set of declarations introduced by the using-declarator is found by performing qualified name lookup (
[basic.lookup.qual]
,[class.member.lookup]
) for the name in the using-declarator, excluding functions that are hidden as described below.
The excluded functions being:
[namespace.udecl]/15
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, 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.
However, when one of the two member function takes its argument as a universal reference, the code compiles fine on all major compilers.
When one of the function takes its argument as a (forwarding) reference, this template function doesn't qualify anymore as hidden since its parameter-type-list differs.
A bug report has been opened by OP, check it out:
Bug 87478 - Hidden member function falsely takes part in qualified name lookup.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With