Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does c++ resolution operator fail to access an ambiguous base? [duplicate]

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.

like image 306
PcAF Avatar asked Apr 21 '16 20:04

PcAF


People also ask

How ambiguity in inheritance can be removed using the scope resolution operator?

You can resolve ambiguity by qualifying a member with its class name using the scope resolution ( :: ) operator. The statement dptr->j = 10 is ambiguous because the name j appears both in B1 and B2 .

What is ambiguity in multiple inheritance how it can be resolved?

An ambiguity can arise when several paths exist to a class from the same base class. This means that a child class could have duplicate sets of members inherited from a single base class. This can be solved by using a virtual base class.

Why there is no ambiguity in multiple inheritance in C++?

Ambiguity in Multiple Inheritance It's because compiler doesn't know which function to call. For example, class base1 { public: void someFunction( ) {....} }; class base2 { void someFunction( ) {....} }; class derived : public base1, public base2 {}; int main() { derived obj; obj.

What is ambiguity error and how do you resolve it?

Ambiguity Errors The inclusion of generics gives rise to a new type of error that you must guard against ambiguity. Ambiguity errors occur when erasure causes two seemingly distinct generic declarations to resolve to the same erased type, causing a conflict. Here is an example that involves method overloading.


3 Answers

Why is it ambiguous? I explicitly specified that I want Top from Right and not from 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. 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();
like image 169
Barry Avatar answered Sep 27 '22 23:09

Barry


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.

like image 37
Potatoswatter Avatar answered Sep 27 '22 22:09

Potatoswatter


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

like image 29
Ardahan Kisbet Avatar answered Sep 27 '22 23:09

Ardahan Kisbet