具有enable_if的C ++模板重载:g ++和clang的不同行为 [英] C++ Template overload with enable_if: different behaviour with g++ and clang

查看:87
本文介绍了具有enable_if的C ++模板重载:g ++和clang的不同行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

During resolution of an overload of a templated member function of a base class, I observed a different behaviour between g++ (5.2.1-23) and clang (3.8.0), with -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++
}

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

The call to Derived<true>::a fails with g++ with the following message:

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 ++中失败,并显示以下消息:

and the call to Derived<false>::a fails with clang++ with the following message:

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.

My guess is that they interpret differently the using Base::a;, and that it isn't considered in clang, whereas it's (maybe too much) considered in g++. What I thought would happen is that if Derived has true as parameter, then the call of a() is dispatched to Derived's implementation, whereas if the parameter is false, the call is dispatched to Base::a.

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

Are they both wrong? Who is right? Who should I submit a bug report to? Can somebody explain what is going on?

谢谢

推荐答案

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

From 3.3.10/p3 Name hiding [basic.scope.hiding]:

在成员函数定义中,在块处声明名称 作用域用相同的值隐藏类成员的声明 姓名;参见3.3.7. 派生类中成员的声明 (第10条)隐藏了 相同的名称; 参见10.2

In a member function definition, the declaration of a name at block scope hides the declaration of a member of the class with the same name; see 3.3.7. The declaration of a member in a derived class (Clause 10) hides the declaration of a member of a base class of the same name; see 10.2

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

Also 7.3.3/p15 The using declaration [namespace.udecl]:

使用声明将基类中的名称带入派生类时 类范围,成员函数和成员函数模板 派生类重写和/或隐藏成员函数和成员 具有相同名称的功能模板,parameter-type-list(8.3.5), 基本类别(相当)中的cv-qualification和ref-qualifier(如果有的话) 而不是冲突). [注意:对于使用命名为a的使用声明 构造函数,请参见12.9. —尾注] [示例:

When a using-declaration brings names from a base class into a derived class scope, 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 (8.3.5), cv-qualification, and ref-qualifier (if any) in a base class (rather than conflicting). [ Note: For using-declarations that name a constructor, see 12.9. — end note ] [Example:

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)
}

—结束示例]

这是在查找成员名称时解决的.因此,这是在模板参数推导之前.因此,无论SFINAE判决如何,正如注释中正确提到的TC一样,隐藏了基本模板功能.

This is resolved during member name look-up. Thus, it's before template argument deduction. Consequently, as correctly TC mentioned in the comments Base template function is hidden no matter of SFINAE verdict.

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

Therefore CLANG is correct and GCC is wrong.

这篇关于具有enable_if的C ++模板重载:g ++和clang的不同行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆