死亡钻石和范围解析运算符 (c++)
Diamond of death and Scope resolution operator (c++)
我有这段代码(菱形问题):
#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
的部分是明确的。这是从b
到Top
的隐式转换是模棱两可的。你必须通过执行以下操作来明确消除你前进方向的歧义:
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 类引用。
您可以在此处找到更多详细信息
- 为什么比较运算符如此快速
- C++映射:具有自定义类的运算符[]不起作用(总是返回0)
- 使用C++中的模板和运算符重载执行矩阵运算
- 为什么这个运算符<重载函数对 STL 算法不可见?
- 增量运算符与后缀混淆
- 一个关于在C++中重载布尔运算符的问题
- 运算符C++ "delete []"仅删除 2 个前值
- 模板类无法识别友元运算符
- 我可以使用条件运算符初始化C风格的字符串文字吗
- 关闭||运算符优化
- 通过继承类使用来自不同命名空间的运算符
- C++Cast运算符过载
- 如何使用AngelScript注册SFML Vector2运算符
- 重载元组索引运算符-C++
- 如何使用重载的相等(==)运算符向测试用例添加描述
- 为什么Mat类的两个对象可以在不重载运算符+的情况下添加
- 多个If语句与使用逻辑运算符计算条件的单个语句的比较
- 布尔比较运算符是如何在C++中工作的
- 重载运算符new[]的行为取决于析构函数
- 如何防止clang格式在流运算符调用之间添加换行符<<