问题
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
TopfromRightand 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