Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SFINAE method completely disables base class's template method in clang

#include <iostream>
#include <utility>

struct B {
    template<typename T, std::enable_if_t<std::is_same<T, int>::value>* = nullptr>
    void foo(T) {
        std::cout<<"B::foo"<<std::endl;
    }
};

struct D: B {        
    using B::foo;
    template<typename T, std::enable_if_t<std::is_same<T, std::string>::value>* = nullptr>
    void foo(T) {
        std::cout<<"D::foo"<<std::endl;
    }
};

int main() {
    D d;
    d.foo(2); // gcc: print "B::foo"; clang: compile error
    return 0;
}

Let's say we want to expose both foo() overloads in derived class D. gcc and Visual Studio compiles and print "B::foo" as I expected. But I get a compile error with clang:

prog.cc:22:7: error: no matching member function for call to 'foo'
    d.foo(2);
    ~~^~~
prog.cc:14:10: note: candidate template ignored: requirement 'std::is_same<int, std::string>::value' was not satisfied [with T = int]
    void foo(T) {

Is this a clang bug? Thanks!

like image 580
Maddie Avatar asked Aug 20 '18 14:08

Maddie


1 Answers

I actually think this is a gcc bug (84832 is closely related, though as Wakely notes it's possible that this should be a core language issue).

From [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.

Where a parameter-type-list is defined in [dcl/fct]/5:

The type of a function is determined using the following rules. The type of each parameter (including function parameter packs) is determined from its own decl-specifier-seq and declarator. After determining the type of each parameter, any parameter of type “array of T” or of function type T is adjusted to be “pointer to T”. After producing the list of parameter types, any top-level cv-qualifiers modifying a parameter type are deleted when forming the function type. The resulting list of transformed parameter types and the presence or absence of the ellipsis or a function parameter pack is the function's parameter-type-list.

B::foo and D::foo have the same name ("foo"), parameter-type-list ([T]), cv-qualification (none), and ref-qualifier (none). Hence, the derived one hides the base one.

like image 112
Barry Avatar answered Nov 19 '22 10:11

Barry