Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Name lookup error of enable_if'd inherited member functions

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.

like image 747
Mario Avatar asked Oct 01 '18 11:10

Mario


1 Answers

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.

like image 175
YSC Avatar answered Oct 17 '22 01:10

YSC