[英]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
fromRight
and not fromLeft
.我明确指定要从
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. 从
b
到Top
的隐式转换是模棱两可的。 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. 但是,您的底层类仍引用了基类(顶层)的两个对象。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.