【问题标题】: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)。其行为与普通成员函数的行为相同,在“可能重复”问题中进行了描述。

标签: c++ operator-overloading multiple-inheritance overloading ambiguous


【解决方案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()定义的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-14
    • 2012-01-29
    • 1970-01-01
    • 1970-01-01
    • 2020-05-20
    相关资源
    最近更新 更多