简体   繁体   English

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

[英]Diamond of death and Scope resolution operator (c++)

I have this code (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();
}

I want to call print() in Top class. 我想在Top类中调用print()

When I try to compile it I get error: 'Top' is an ambiguous base of 'Bottom' on this line: b.Right::Top::print(); 当我尝试对其进行编译时,出现错误: 'Top' is an ambiguous base of 'Bottom'此行上'Top' is an ambiguous base of 'Bottom'b.Right::Top::print(); Why is it ambiguous? 为什么模棱两可? I explicitly specified that I want Top from Right and not from Left . 我明确指定要从Right Top Right而不是“ Top Left

I don't want to know HOW to do it, yes it can be done with references, virtual inheritance, etc. I just want to know why is b.Right::Top::print(); 我不想知道如何去做,是的,它可以通过引用,虚拟继承等来完成。我只是想知道为什么b.Right::Top::print(); ambiguous. 暧昧。

Why is it ambiguous? 为什么模棱两可? I explicitly specified that I want Top from Right and not from Left . 我明确指定要从Right Top Right而不是“ Top Left

That was your intent, but that's not what actually happens. 那是您的意图,但实际上并非如此。 Right::Top::print() explicitly names the member function that you want to call, which is &Top::print . Right::Top::print()明确命名您要调用的成员函数,即&Top::print But it does not specify on which subobject of b we are calling that member function on. 但是它没有指定我们在b哪个子对象上调用该成员函数。 Your code is equivalent conceptually to: 您的代码在概念上等效于:

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

The part that selects print is unambiguous. 选择print的部分是明确的。 It's the implicit conversion from b to Top that's ambiguous. bTop的隐式转换是模棱两可的。 You'd have to explicitly disambiguate which direction you're going in, by doing something like: 您必须通过执行以下操作来明确消除前进的方向:

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

The scope resolution operator is left-associative (though it doesn't allow parentheses). 范围解析运算符是左关联的(尽管它不允许使用括号)。

So whereas you want to refer to A::tell inside B , the id-expression refers to tell inside B::A , which is simply A , which is ambiguous. 因此,虽然您想在B内部引用A::tell ,但id-expression却在B::A内部tell ,这就是A ,这是模棱两可的。

The workaround is to first cast to the unambiguous base B , then cast again to A . 解决方法是先强制转换为明确的基B ,然后再次强制转换为A

Language-lawyering: 语言咨询:

[basic.lookup.qual]/1 says, [basic.lookup.qual] / 1说,

The name of a class or namespace member or enumerator can be referred to after the :: scope resolution operator applied to a nested-name-specifier that denotes its class, namespace, or enumeration. 在将::作用域解析运算符应用于表示其类,名称空间或枚举的嵌套名称说明符之后, ::类或名称空间成员或枚举器的名称

The relevant grammar for nested-name-specifier is, 嵌套名称说明符的相关语法为:

nested-name-specifier: 嵌套名称说明符:

type-name :: 类型名称 ::

nested-name-specifier identifier :: 嵌套名称说明 标识符 ::

So, the first nested-name-specifier is B:: and A is looked up within it. 因此,第一个嵌套名称说明符B::并且在其中查找A Then B::A is a nested-name-specifier denoting A and tell is looked up within it. 然后B::A是表示A的嵌套名称说明符,并在其中查找tell

Apparently MSVC accepts the example. 显然,MSVC接受该示例。 Probably it has a nonstandard extension, to resolve ambiguity by backtracking through such specifiers. 它可能具有非标准扩展,可以通过在此类说明符中回溯来解决歧义。

Actually, giving code is working fine as I tried it on Visual Studio 2019. There are two way to solve Diamond Problem; 实际上,正如我在Visual Studio 2019上尝试过的那样,提供代码可以正常工作。有两种解决钻石问题的方法; - Using Scope resolution operator - Inherit base class as virtual -使用范围解析运算符-将基类继承为虚拟

Calling print function by b.Right::Top::print() should be executed with no errors. 通过b.Right::Top::print()调用print函数应正确执行。 But there is still two objects of your base class (Top) referred from your Bottom class. 但是,您的底层类仍引用了基类(顶层)的两个对象。

You can find additional detail in here 您可以在这里找到更多详细信息

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM