使用enable_if的C ++模板重载:使用g ++和clang

时间:2015-12-08 14:51:16

标签: c++ templates overloading c++14

在解析基类的模板化成员函数的重载期间,我观察到g ++(5.2.1-23)和clang(3.8.0)与-std=c++14之间的不同行为。

#include <iostream>
#include <type_traits>

struct Base
{
  template <typename T>
  auto a(T t) -> void {
    std::cout<< "False\n";
  }
};

template <bool Bool>
struct Derived : public Base
{

  using Base::a;
  template <typename T, bool B = Bool>
  auto a(T t) -> std::enable_if_t<B, void>
  {
    std::cout<< "True\n";
  }
};

int main()
{
  Derived<true> d;
  d.a(1); // fails with g++, prints "true" with clang
  Derived<false> d2;
  d2.a(1); // fails with clang++, prints "false" with g++
}

Derived<true>::a的调用因g ++而失败,并显示以下消息:

test.cc: In function ‘int main()’:
test.cc:28:8: error: call of overloaded ‘a(int)’ is ambiguous
   d.a(1);
        ^
test.cc:18:8: note: candidate: std::enable_if_t<B, void> Derived<Bool>::a(T) [with T = int; bool B = true; bool Bool = true; std::enable_if_t<B, void> = void]
   auto a(T t) -> std::enable_if_t<B, void>
        ^
test.cc:7:8: note: candidate: void Base::a(T) [with T = int]
   auto a(T t) -> void {
        ^

并且对Derived<false>::a的调用因clang ++而失败,并显示以下消息:

test.cc:32:6: error: no matching member function for call to 'a'
  d2.a(1);
  ~~~^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.2.1/../../../../include/c++/5.2.1/type_traits:2388:44: note: candidate template ignored: disabled by 'enable_if' [with T = int, B = false]
    using enable_if_t = typename enable_if<_Cond, _Tp>::type;
                                           ^

我的猜测是,他们对using Base::a;的解释不同,并且在clang中没有考虑它,而在g ++中则考虑它(可能太多)。我认为如果Derivedtrue作为参数,那么a()的调用将被调度到Derived的实现,而如果参数是false,调用将发送到Base::a

他们都错了吗?谁是对的?我应该向谁提交错误报告?有人可以解释发生了什么吗?

由于

1 个答案:

答案 0 :(得分:3)

从3.3.10 / p3名称隐藏[basic.scope.hiding]:

  

在成员函数定义中,在块处声明名称   scope隐藏类的成员声明   名称;见3.3.7。 派生类中成员的声明   (第10条)隐藏了一个基类成员的声明   同名; 见10.2

同样7.3.3 / p15使用声明[namespace.udecl]:

  

当using声明将基类中的名称带入派生类时   类范围,成员函数和成员函数模板   派生类覆盖和/或隐藏成员函数和成员   具有相同名称的函数模板,参数类型列表(8.3.5),   基类中的cv-qualification和ref-qualifier(如果有的话)(相反)   而不是冲突)。 [注意:对于使用命名的声明   构造函数,见12.9。 - 尾注] [例子:

struct B {
  virtual void f(int);
  virtual void f(char);
  void g(int);
  void h(int);
};
struct D : B {
  using B::f;
  void f(int); // OK: D::f(int) overrides B::f(int);
  using B::g;
  void g(char); // OK
  using B::h;
  void h(int); // OK: D::h(int) hides B::h(int)
};
void k(D* p)
{
  p->f(1); // calls D::f(int)
  p->f(’a’); // calls B::f(char)
  p->g(1); // calls B::g(int)
  p->g(’a’); // calls D::g(char)
}
     

- 结束示例]

在成员名称查找期间解决了这个问题。因此,它在模板参数推断之前。因此,正如评论中提到的正确的TC基本模板功能是隐藏的,无论SFINAE判决如何。

因此CLANG是正确的,GCC是错误的。