死亡钻石和范围解析运算符 (c++)

Diamond of death and Scope resolution operator (c++)

本文关键字:运算符 c++ 钻石 范围      更新时间:2023-10-16

我有这段代码(菱形问题):

#include <iostream>
using namespace std;
struct Top
{
    void print() { cout << "Top::print()" << endl; }
};
struct Right : Top 
{
    void print() { cout << "Right::print()" << endl; }
};
struct Left : Top 
{
    void print() { cout << "Left::print()" << endl; }
};
struct Bottom: Right, Left{};
int main()
{
    Bottom b;
    b.Right::Top::print();
}

我想在课堂上打电话给print() Top

当我尝试编译它时,出现错误:'Top' is an ambiguous base of 'Bottom'在这一行:b.Right::Top::print(); 为什么模棱两可?我明确指出我希望从Right而不是Left Top.

我不想知道该怎么做,是的,它可以通过引用、虚拟继承等来完成。我只想知道为什么b.Right::Top::print();模棱两可。

为什么模棱两可?我明确指定我希望从Right而不是Left Top.

那是你的意图,但事实并非如此。 Right::Top::print()显式命名要调用的成员函数,即 &Top::print 。但它没有指定我们在b的哪个子对象上调用该成员函数。您的代码在概念上等效于:

auto print = &Bottom::Right::Top::print;  // ok
(b.*print)();                             // error

选择print的部分是明确的。这是从bTop的隐式转换是模棱两可的。你必须通过执行以下操作来明确消除你前进方向的歧义:

static_cast<Right&>(b).Top::print();

范围解析运算符是左关联的(尽管它不允许使用括号)。

所以,虽然你想引用B内部的A::tell,但id表达式指的是B::A内部的tell,这很简单A,这是模棱两可的。

解决方法是首先强制转换为明确的基B,然后再次转换为A

语言律师:

[basic.lookup.qual]/1 说,

类或命名空间

成员或枚举器的名称可以在应用于表示其类、命名空间或枚举的嵌套名称说明符::范围解析运算符之后引用。

嵌套名称说明符的相关语法是,

嵌套名称说明符:

类型名称::

嵌套名称说明符 标识符::

因此,第一个嵌套名称说明符B::A在其中查找。然后B::A是一个嵌套名称说明符,表示A并在其中查找tell

显然MSVC接受了这个例子。可能它有一个非标准的扩展,通过回溯这些说明符来解决歧义。

实际上

,当我在Visual Studio 2019上尝试过时,提供代码可以正常工作。有两种方法可以解决钻石问题; - 使用范围解析运算符 - 将基类继承为虚拟

通过b.Right::Top::print()调用打印函数应该执行,没有错误。但是,基类 (Top) 中仍有两个对象从 Bottom 类引用。

您可以在此处找到更多详细信息