以下是示例代码:
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
据我所知,它寻找运营商&amp;按名称重载,而不是通过签名,因此它会发现模糊的重载并产生错误。
问题是:标准是否正确?如果没有,哪一段描述它?有没有办法让GCC接受这个代码(我的意思是,通过签名而不是名字查找)。
BTW,我知道,如何修复此代码。我只是想知道,为什么会出现错误。
答案 0 :(得分:4)
您所造成的是钻石继承问题,您可以使用虚拟继承来解决它。
在A中,您已声明虚拟operator&
,该B
也在C
和D
中定义。现在,当您使用多重继承时,这两种方法都在L L
| |
A B
\ /
C
内定义。
来自标准(10.1 多个基类)。
不应将类多次指定为派生类的直接基类。 [注意:一个班级可以 不止一次是间接基类,可以是直接基类和间接基类。有限 可以用这样一个类完成的事情。非静态数据成员和成员函数的直接 基类不能在派生类的范围内引用。但是,静态成员,枚举 和类型可以明确地引用。 - 尾注] [例子:
和
不包含关键字virtual的基类规范指定非虚基类。一个基地 包含关键字virtual的类指定器,指定虚拟基类。对于每个不同的事件 在最派生类的类格子中的非虚基类,最派生的对象(1.8)应该是 包含该类型的相应的不同基类子对象。对于每个不同的基类 指定虚拟,最派生的对象应包含该类型的单个基类子对象。 [例: 对于类类型C的对象,类格子中的每个不同的(非虚拟)基类L出现 C在C类对象中与一个不同的L子对象一一对应。给定C类定义 如上所述,C类对象将有两个L类子对象,如下所示。
{{1}}
图3 - 非虚拟基础 5在这种格子中,可以使用显式定性来指定哪个子对象的含义。功能的身体 C :: f可以引用每个L子对象的下一个成员: void C :: f(){A :: next = B :: next; } //形式良好 如果没有A ::或B ::限定符,由于模糊性(10.2),上面C :: f的定义将是不正确的。
答案 1 :(得分: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节)。
答案 2 :(得分:0)
此代码实现的目标称为diamond-inheritance
问题。简要解释一下,在编译期间,编译器发现operator &
不明确,因为它无法确定是调用B类继承版本还是C类继承版本。
要解决此问题,请将您的班级定义声明为
struct B : virtual public A {};
只能在D类中提供您的功能的一个副本。