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.
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();
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.
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
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.