调用过载操作员时出现歧义

Ambigulty while calling overloaded operator

本文关键字:歧义 操作员 调用      更新时间:2023-10-16

以下是示例代码:

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;
}

它在VS2008中完美工作,但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

就我所见,它在寻找运营商&按名称重载,而不是按签名重载,因此它会发现不明确的重载并产生错误。

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

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

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

在A中,您已经声明了虚拟operator&,它也在BC中定义。现在,这两种方法都是在D中定义的,因为您正在使用多重继承。

来自标准(10.1多个基类)。

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

不包含关键字virtual的基类指定器指定非虚拟基类。基地类指定器包含关键字virtual,指定一个虚拟基类。对于每个不同的事件对于最派生类的类格中的非虚拟基类,最派生对象(1.8)应包含该类型的相应的不同基类子对象。对于每个不同的基类指定的虚拟对象,派生最多的对象应包含该类型的单个基类子对象。[示例:对于类类型C的对象,(非虚拟)基类L在的类格中的每次不同出现C与类型C的对象中的一个不同的L子对象一一对应。给定定义的类C如上所述,类C的对象将具有两个类L的子对象,如下所示。

L       L
|       |
A       B
     /
    C

图3——非虚拟基础5在这种格中,可以使用显式定性来指定所指的子对象。函数体C: :f可以引用每个L子对象旁边的成员:void C::f(){A::next=B::next;}//形式良好如果没有A::或B::限定符,上述C::f的定义将因歧义而不正确(10.2)。

我在Standard中发现了什么:

3.4名称查找[基本查找]

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

此代码所实现的内容称为diamond-inheritance问题。简单解释一下,在编译过程中,编译器发现operator &不明确,因为它无法判断是调用B类继承版本还是C类继承版本。

要克服这一点,请将类定义声明为

struct B : virtual public A {};

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