463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 123094

Do Derived1::Base and Derived2::Base refer to the same type?

MSVC, Clang and GCC disagree on this code:

struct Base { int x; };
struct Der1 : public  Base {};
struct Der2 : public  Base {};

struct AllDer : public Der1, public Der2 {
    void foo() {
        Der1::Base::x = 5;
    }
};

Godbolt

GCC:

<source>: In member function 'void AllDer::foo()':    
<source>:10:21: error: 'Base' is an ambiguous base of 'AllDer'    
   10 |         Der1::Base::x = 5;    
      |                     ^    
Compiler returned: 1

Clang gives a similar error, and MSVC gives no error.

Who is right here?

I suppose this is covered in [class.member.lookup], but I have difficulties understanding what it is trying to tell me for this case. Please quote the relevant parts and if possible explain in plain English.

PS: Inspired by this question Why is Reference to Base Class ambiguous with :: -operator trough derived class?

PPS: Actually my doubt is whether Der1::Base refers to the type, that would be Base (and then Der2::Base is exactly the same type), or to the subobject. I am convinced that it is the first, but if it is the latter then MSVC would be right.

Upvotes: 15

Views: 305

Answers (2)

MSalters
MSalters

Reputation: 180020

To answer the question in the title, yes, Derived1::Base references the injected-class-name [class.pre] Base and so does Derived2::Base. Both refer to the class ::Base.

Now, if Base would have a static member x, then the lookup of Base::x would be unambiguous. There's only one.

The problem in this example is that x is a non-static member, and AllDer has two such members. You can disambiguate such access to x by specifying an unambiguous base class of AllDer which has only one x member. Derived1 is an unambiguous base class, and it has one x member, so Derived1::x unambiguously specifies which of the two x members in AllDer you mean. Base too has only one x member, but it is not an unambiguous base of AllDer. Every instance of AllDer has two sub-objects of type Base. Therefore Base::x is ambiguous in your example. And since Derived1::Base is just another name for Base, this remains ambiguous.

[class.member.lookup] specifies that x is looked up in the context of the nested-name-specifier, so that has to be resolved first. We are indeed looking for Base::x, not Derived1::x, because we started by resolving Derived1::Base as Base. This part succeeds, there's only one x in Base. Note 12 in [class.member.lookup] explicitly tells you that an using an unambiguous name lookup may still fail when there are multiple subobjects with that same name. D::i in that example is basically your Base::x.

Upvotes: 6

Daniel
Daniel

Reputation: 31579

The reason you can refer to the class name as a member of the class is because cpp aliases it for convenient use, as if you wrote using Base = ::Base; inside Base.
The problem you’re facing is that Der1::Base is Base.
Thus, when you write Der1::Base::x, it’s the same as Base::x.

Upvotes: 2

Related Questions