2

这是示例代码:

struct A
{
    virtual int operator & ( A & ) { return 0; }
};

struct B : public A {};
struct C : public A {};

struct D : public C, public B {};

int main()
{
    D d;
    std::cout << &d << std::endl;
    return 0;
}

它在 VS 2008 中完美运行,但 GCC 无法编译它:

../src/TestCast.cpp: In function ‘int main()’:
../src/TestCast.cpp:26:16: error: request for member ‘operator&’ is ambiguous
../src/TestCast.cpp:15:14: error: candidates are: virtual int A::operator&(A&)
../src/TestCast.cpp:15:14: error:                 virtual int A::operator&(A&)
make: *** [src/TestCast.o] Error 1

据我所知,它通过名称而不是签名来查找 operator& 重载,因此它会发现模棱两可的重载并产生错误。

问题是:标准是否正确?如果不是,哪一段描述了它?有什么方法可以让 GCC 接受这个代码(我的意思是,通过签名而不是通过名称查找)。

顺便说一句,我知道如何修复此代码。我只想知道,为什么会出现错误。

4

3 回答 3

4

您造成的是钻石继承问题,您可以通过虚拟继承解决它。

在 A 中,您已经声明了 virtual operator&,它也在B和中定义CD现在,当您使用多重继承时,这两种方法都在内部定义。

从标准(10.1多个基类)。

一个类不得多次指定为派生类的直接基类。[注意:一个类可以多次作为间接基类,可以是直接基类和间接基类。这样的课程可以做的事情有限。不能在派生类的范围内引用直接基类的非静态数据成员和成员函数。但是,可以明确地引用静态成员、枚举和类型。— 尾注] [示例:

不包含关键字 virtual 的基类说明符指定非虚拟基类。包含关键字 virtual 的基类说明符指定一个虚拟基类。对于最派生类的类格中非虚拟基类的每次不同出现,最派生对象(1.8)应包含该类型的相应不同基类子对象。对于每个指定为虚拟的不同基类,最派生对象应包含该类型的单个基类子对象。[示例:对于 C 类类型的对象,C 的类格中(非虚拟)基类 L 的每次不同出现与 C 类型对象内的不同 L 子对象一一对应。鉴于上面定义的类 C,类 C 的一个对象将有两个类 L 的子对象,如下所示。

L       L
|       |
A       B
  \   /
    C

图 3 — 非虚基 5 在这样的格中,可以使用显式限定来指定所指的子对象。函数 C::f 的主体可以引用每个 L 子对象的成员 next: void C::f() { A::next = B::next; } // 格式良好 如果没有 A:: 或 B:: 限定符,上述 C::f 的定义会因为歧义而成为格式错误 (10.2)。

于 2013-08-23T12:26:28.240 回答
0

我在标准中发现的内容:

3.4 名称查找 [basic.lookup]

1 名称查找规则统一适用于所有名称(包括 typedef-names (7.1.3)、namespace-names (7.3) 和 class-names (9.1)),只要文法允许在特定规则讨论的上下文中使用此类名称。名称查找将名称的使用与该名称的声明 (3.1) 相关联。名称查找应找到名称的明确声明(见 10.2)。如果名称查找发现名称是函数名称,则名称查找可能会将多个声明与名称相关联;据说这些声明形成了一组重载函数(13.1)。名称查找成功后发生重载解析 (13.3)。只有在名称查找和函数重载解析(如果适用)成功后才考虑访问规则(第 11 条)。只有在名称查找、函数重载解析(如果适用)和访问检查成功之后,名称声明引入的属性才会在表达式处理中进一步使用(第 5 条)。

于 2013-08-23T12:37:37.530 回答
0

这段代码实现了什么叫做diamond-inheritance问题。简单解释一下,在编译过程中,编译器发现operator &歧义,因为它无法确定是调用B类继承版本还是C类继承版本。

为了克服这个问题,将你的类定义声明为

struct B : virtual public A {};

这使得 D 类中只有一个函数副本可用。

于 2013-08-23T13:29:00.150 回答