为啥 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
:事实上,错误信息具有误导性,因为现在Ea
和Eb
是两个不同的类,从Ea
到Eb
的没有隐式转换,不应该出现歧义,但是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 函数,但允许我包含它们所属的库?
为啥具有相同名称但不同签名的多个继承函数不会被视为重载函数?