[MSVC,Clang和GCC在此代码上存在分歧:
struct Base { int x; };
struct Der1 : public Base {};
struct Der2 : public Base {};
struct AllDer : public Der1, public Der2 {
void foo() {
Der1::Base::x = 5;
}
};
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给出了类似的错误,而MSVC没有给出错误。
谁在这里?
我想这已经在[class.member.lookup]中涵盖了,但是我很难理解它在这种情况下试图告诉我什么。请引用相关部分,并在可能的情况下以简明的英语进行解释。
PS:受此问题启发Why is Reference to Base Class ambiguous with :: -operator trough derived class?
PPS:实际上,我的疑问是Der1::Base
是指类型,也就是Base
(然后Der2::Base
是完全相同的类型)还是子对象。我坚信这是第一个,但是如果是后者,那么MSVC是正确的。
要回答标题中的问题,是的,Derived1::Base
引用了injected-class-name [class.pre] Base
,Derived2::Base
也引用了。两者都引用类::Base
。
现在,如果Base
将具有static成员x
,则对Base::x
的查找将是明确的。只有一个。
此示例中的问题是x
是非静态成员,并且AllDer
具有two这样的成员。您可以通过指定只有一个x
成员的AllDer
的unambiguous基类来消除对x
的此类访问。 Derived1
是明确的基类,并且具有一个x
成员,因此Derived1::x
明确指定了x
中两个AllDer
成员中的哪个。 Base
也只有一个x
成员,但这并不是AllDer
的明确基础。 AllDer
的每个实例都有两个Base
类型的子对象。因此,Base::x
在您的示例中是不明确的。而且由于Derived1::Base
只是Base
的别称,所以仍然模棱两可。
[class.member.lookup]指定在嵌套名称说明符的上下文中查找x
,因此必须首先对其进行解析。我们确实在寻找Base::x
,而不是Derived1::x
,因为我们首先将Derived1::Base
解析为Base
。这部分成功了,在x
中只有一个Base.
。[class.member.lookup]中的注释12明确告诉您,当存在多个具有相同名称的子对象时,使用明确的名称查找可能仍然会失败。在该示例中,D::i
基本上就是您的Base::x
。
之所以可以将类名称称为类的成员,是因为cpp为方便使用起了别名,就像您在Base中编写了using Base = ::Base;
。您面临的问题是Der1::Base
为Base
。因此,当您写入Der1::Base::x
时,它与Base::x
相同。