12

可能重复:
为什么具有相同名称但不同签名的多个继承函数不会被视为重载函数?

这无法使用 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 编译。

4

2 回答 2

5

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

无论如何,如果你将函数带入类范围,明确地写成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

于 2011-11-10T17:38:26.977 回答
4

试图理解标准中的实际文本(第 10.2 节)并不容易,但有一个例子说明了这一点:x 如果派生类中不存在该名称,则派生类中名称的名称查找失败,但它存在于多个基类中,并且没有隐藏。(隐藏在这里不相关,因为它只在存在虚拟继承时进行干预。)据我所知,无论成员的名称如何,情况都是如此。如果成员碰巧有特殊的名字,我找不到任何例外operator()。重载解析不起作用,因为它们在重载集完全构建之前名称查找失败。我很确定这两个代码片段都是非法的,并且在 clang 中有一个错误。

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

于 2011-11-10T17:57:23.407 回答