6

Microsoft compiler (Visual Studio 2017 15.2) rejects the following code:

#include <type_traits>

struct B
{ 
    template<int n, std::enable_if_t<n == 0, int> = 0>
    void f() { }
};

struct D : B
{
    using B::f; 
    template<int n, std::enable_if_t<n == 1, int> = 0>
    void f() { }
};

int main()
{
    D d;
    d.f<0>();
    d.f<1>();
}

The error is:

error C2672: 'D::f': no matching overloaded function found
error C2783: 'void D::f(void)': could not deduce template argument for '__formal'
note: see declaration of 'D::f'

Clang also rejects it:

error: no matching member function for call to 'f'
    d.f<0>();
    ~~^~~~
 note: candidate template ignored: disabled by 'enable_if' [with n = 0]
    using enable_if_t = typename enable_if<_Cond, _Tp>::type;

GCC perfectly accepts it. Which compiler is right?

Addition:

With SFINAE in the form

template<int n, typename = std::enable_if_t<n == 0>>
...
template<int n, typename = std::enable_if_t<n == 1>>

GCC also produces an error:

error: no matching function for call to ‘D::f<0>()’
 d.f<0>();
        ^
note: candidate: template<int n, class> void D::f()
 void f()
      ^
note:   template argument deduction/substitution failed:
Evg
  • 25,259
  • 5
  • 41
  • 83
  • Unrelated: You probably want to use a virtual function to differentiate between base and derived method rather than SFINAE. – Henri Menke Aug 08 '17 at 19:24
  • @HenriMenke I don't know what the original use case was, and neither do you, but virtual functions achieve something completely different from what is being shown here. This is leveraging implementation inheritance, not polymorphism, and it makes both functions available to users of D. virtual is about polymorphism, and it would only have one function available to users of D. – Nir Friedman Aug 08 '17 at 19:27
  • @Jarod42 Fixed tag, thanks. – Evg Aug 08 '17 at 19:35
  • 4
    With, `template>`, you declare2 same functions (with different default parameter). – Jarod42 Aug 08 '17 at 19:38
  • 6
    [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).](http://eel.is/c++draft/namespace.udecl#15.sentence-1) :/ – cpplearner Aug 08 '17 at 19:43
  • @cpplearner Please post as an answer. – Barry Aug 08 '17 at 19:50
  • @Barry And is it an answer? These two functions have different parameter-type-lists. – Evg Aug 08 '17 at 19:59
  • 1
    @Evgeny: template parameter seems not to count. – Jarod42 Aug 08 '17 at 20:06
  • 2
    @Evgeny No, they don't - they both have the same *parameter-type-list*: no parameters. – Barry Aug 08 '17 at 20:10
  • 3
    I messaged Mike on this. Note that there was a similar issue for the paragraph preceding 15, that was resolved as core issue 565. – Columbo Aug 09 '17 at 15:11
  • @Columbo Does no one in core read the forum? Brian posted about this [two days ago](https://groups.google.com/a/isocpp.org/d/msg/std-discussion/7Z1W41eotCY/OUQQQ6_oAgAJ) as well. – Barry Aug 10 '17 at 19:55

1 Answers1

5

Turning cppleaner's comment into an answer:

From namespace.udecl#15.sentence-1:

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)

Unfortunately, template parameter doesn't count and both f has empty parameter-type-list, are not const and no ref-qualifier.

Derived::f so hides Base::f.

gcc is wrong to accept that code.

So the way to fix it is by default argument (returned type doesn't count either):

struct B
{ 
    template <int n>
    void f(std::enable_if_t<n == 0>* = nullptr) { }
};

struct D : B
{
    using B::f; 
    template <int n>
    void f(std::enable_if_t<n == 1>* = nullptr) { }
};
Jarod42
  • 203,559
  • 14
  • 181
  • 302