问题
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.
When I try to compile it I get error: 'Top' is an ambiguous base of 'Bottom'
on this line: b.Right::Top::print();
Why is it ambiguous? I explicitly specified that I want Top
from Right
and not from 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();
ambiguous.
回答1:
Why is it ambiguous? I explicitly specified that I want
Top
fromRight
and not fromLeft
.
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
. But it does not specify on which subobject of b
we are calling that member function on. Your code is equivalent conceptually to:
auto print = &Bottom::Right::Top::print; // ok
(b.*print)(); // error
The part that selects print
is unambiguous. It's the implicit conversion from b
to Top
that's ambiguous. You'd have to explicitly disambiguate which direction you're going in, by doing something like:
static_cast<Right&>(b).Top::print();
回答2:
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.
The workaround is to first cast to the unambiguous base B
, then cast again to A
.
Language-lawyering:
[basic.lookup.qual]/1 says,
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. Then B::A
is a nested-name-specifier denoting A
and tell
is looked up within it.
Apparently MSVC accepts the example. Probably it has a nonstandard extension, to resolve ambiguity by backtracking through such specifiers.
回答3:
Actually, giving code is working fine as I tried it on Visual Studio 2019. There are two way to solve Diamond Problem; - Using Scope resolution operator - Inherit base class as virtual
Calling print function by b.Right::Top::print()
should be executed with no errors. But there is still two objects of your base class (Top) referred from your Bottom class.
You can find additional detail in here
来源:https://stackoverflow.com/questions/36779466/diamond-of-death-and-scope-resolution-operator-c