问题
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.
回答1:
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
.
回答2:
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
.
来源:https://stackoverflow.com/questions/61477009/do-derived1base-and-derived2base-refer-to-the-same-type