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

时间:2016-04-21 20:09:14

标签: c++ inheritance multiple-inheritance diamond-problem

我有这个代码(钻石问题):

#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(); 为什么它含糊不清?我明确指出我需要来自Top的{​​{1}},而不是来自Right

我不想知道怎么做,是的,可以通过引用,虚拟继承等来完成。我只是想知道为什么Left不明确。

3 个答案:

答案 0 :(得分:15)

  

为什么它含糊不清?我明确指出我需要来自Top的{​​{1}},而不是来自Right

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

b

选择auto print = &Bottom::Right::Top::print; // ok (b.*print)(); // error 的部分是明确的。这是从printb的隐含转换模糊不清。您必须通过以下操作明确消除您进入的方向的歧义:

Top

答案 1 :(得分:4)

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

因此,虽然您想在A::tell内引用B,但id-expression指的是tell内的B::A,它只是A,其中很暧昧。

解决方法是首先投射到明确的基础B,然后再投射到A

语言 - 律师业:

[basic.lookup.qual] / 1说,

  

::范围解析运算符应用于表示其类,命名空间或 nested-name-specifier ::范围解析运算符之后,可以引用类或命名空间成员或枚举数的名称。枚举。

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

  

嵌套名称说明符:

     

type-name ::

     

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

因此,第一个嵌套名称说明符A,并在其中查找B::A。然后A是一个嵌套名称说明符,表示tell,并在其中查找parser.add_argument('--ensure', nargs='*', default=None) ENSURE = config.ensure is None

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

答案 2 :(得分:-1)

实际上,正如我在Visual Studio 2019上尝试的那样,提供代码可以正常工作。 有两种解决钻石问题的方法:  -使用范围解析运算符  -继承基类为虚拟

通过b.Right::Top::print()调用打印功能应正确执行。但是,您的底层类仍引用了基类(顶层)的两个对象。

您可以在here

中找到其他详细信息