为啥 GCC 不能消除多个继承函数的歧义(但 clang 可以)? [复制]

Posted

技术标签:

【中文标题】为啥 GCC 不能消除多个继承函数的歧义(但 clang 可以)? [复制]【英文标题】:Why can't GCC disambiguate multiple inherited functions (yet clang can)? [duplicate]为什么 GCC 不能消除多个继承函数的歧义(但 clang 可以)? [复制] 【发布时间】:2011-12-26 09:08:12 【问题描述】:

可能重复:Why do multiple-inherited functions with same name but different signatures not get treated as overloaded functions?

使用 g++ 4.6.1 在指定位置编译失败:

enum Ea  Ea0 ;
enum Eb  Eb0 ;

struct Sa  void operator()(Ea)  ;
struct Sb  void operator()(Eb)  ;
struct Sbroken : Sa, Sb ;

struct Sworks 
    void operator()(Ea) 
    void operator()(Eb) 
;

int main() 
    Sworks()(Ea0);
    Sbroken()(Ea0); // g++ can't disambiguate Ea vs. Eb

Clang 2.8 确实编译了这段代码,这让我不确定代码是否真的是有效的 C++。我正要乐观的断定clang是对的,g++是错的,但是后来我做了一个小改动,让clang也出现了类似的错误:

enum Ea  Ea0 ;
enum Eb  Eb0 ;

struct Sa  void f(Ea)  ;
struct Sb  void f(Eb)  ;
struct Sbroken : Sa, Sb ;

struct Sworks 
    void f(Ea) 
    void f(Eb) 
;

int main() 
    Sworks().f(Ea0);
    Sbroken().f(Ea0); // both clang and g++ say this is ambiguous

我在那里所做的唯一更改是使用命名函数f 而不是operator()。我不明白这为什么重要,但确实如此:这个版本不能用 g++ 或 clang 编译。

【问题讨论】:

你能看一下clang生成的代码,看看它是否做了什么有意义的事情吗? Comeau 的编译器(非常符合标准)在您的第一个 sn-p 上也会失败并出现歧义错误。 答案:来自N3242: 13.3.1.1.2 类类型对象的调用[over.call.object]/1"T的函数调用操作符通过普通查找得到在(E).operator() 的上下文中名称为operator()”因此Sbroken()(Ea0) 被解释为Sbroken().operator(Ea0)。其行为与普通成员函数的行为相同,在“可能重复”问题中进行了描述。 【参考方案1】:

我认为这与隐藏基类中的函数有关,而且 GCC 的错误消息似乎没有多大帮助,即使您使用 struct 而不是 enum :事实上,错误信息具有误导性,因为现在EaEb 是两个不同的类,从EaEb没有隐式转换,不应该出现歧义,但是GCC似乎不同意我的观点:http://ideone.com/cvzLW(另见修改)。

无论如何,如果您将函数带入类范围,明确using写为:

struct Sbroken : Sa, Sb 

   using Sa::operator();
   using Sb::operator();
;

然后它工作:http://ideone.com/LBZgC

与其他示例相同:

struct Sbroken : Sa, Sb 

   using Sa::f;
   using Sb::f;
;

代码:http://ideone.com/3hojd

【讨论】:

【参考方案2】:

试图理解标准(§10.2)中的实际文本并不容易, 但是有一个例子可以清楚地说明:name lookup for a name x 如果派生类中不存在名称,则派生类中的失败 类,但它存在于多个基类中,而且它不是 隐。 (隐藏在这里不相关,因为它只在虚拟时干预 存在继承。)据我所知,情况就是这样 不论会员的姓名;如果 成员恰好具有特殊名称operator()。超载 决议没有发挥作用,因为他们的名义是失败 查找,在重载集完全建立之前。我相当确定 代码的两个 sn-ps 都是非法的,并且存在错误 铿锵声。

您可以使用using 声明将名称注入派生的 类,或者您可以在派生中显式定义转发运算符 班级。一旦在派生类中找到名称,编译器就会停止, 并且不查看基类。

【讨论】:

如果程序格式不正确并且不需要发出诊断信息,那么发出一些损坏的代码是否完全在它的权利范围内? @awoodland 我在标准中没有看到任何说明未定义行为的内容。需要进行诊断。 (一旦发出诊断信息,编译器当然可以做任何事情。) "如果成员碰巧有特殊名称operator(),我找不到异常"函数调用是用.operator()定义的。

以上是关于为啥 GCC 不能消除多个继承函数的歧义(但 clang 可以)? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

消除多重继承中的类成员歧义

为啥我的 gcc 编译器不能识别 bzip2 函数,但允许我包含它们所属的库?

为啥具有相同名称但不同签名的多个继承函数不会被视为重载函数?

消除 RESTful 服务的歧义

gcc 和 clang 抛出“没有匹配的函数调用”但 msvc (cl) 编译并按预期工作

RPC(或者:如何根据 TypeRep 值消除函数应用程序的歧义?)