class A { public: int a; };
class B : public virtual A { public: using A::a; };
class C : public virtual A { public: using A::a; };
class D : public C, public B { };
class W { public: int w; };
class X : public virtual W { public: using W::w; };
class Y : public virtual W { };
class Z : public Y, public X { };
int main(){
D d;
d.a = 0; // Error
Z z;
z.w = 0; // Correct
return 0;
}
第一组类声明(A
,B
,C
和D
)以及第二组(W
,X
, Y
和Z
)的构建类似,只是class C
有使用声明(using A::a
)且class Y
没有。
尝试访问a
中的成员d.a = 0
时,我在Clang(error: member 'a' found in multiple base classes of different types
)和GCC(error: request for member 'a' is ambiguous
)中收到错误消息。我检查了两个编译器的最新版本和旧版本,所有这些都是一致的。但是,在w
中访问z.w = 0
会成功编译。
访问a
时出现歧义的原因是什么?
据我所知,类B
和C
中的访问声明都指向相同的基类成员。顺便说一句,如果我删除它们,测试会成功编译,因为a
已经可公开访问(public
访问说明符)。
提前致谢。
注意:以上代码是来自SolidSands'的稍加修改的测试。 SuperTest套件。
答案 0 :(得分:1)
这里有实施差异; ICC接受您的代码,而gcc,clang和MSVC拒绝它。 ICC是正确的,其他编译器是不正确的。
为<a target="_blank" href="#"><li><img src="http://icons.iconarchive.com/icons/danleech/simple/64/facebook-icon.png" class="facebook"></li></a>
<a target="_blank" href="#"><li><img src="https://awesomeshop.target.com/img/instagram_logo.png" class="instagram"></li>
<a target="_blank" href="#"><li><img src="https://cdn.dribbble.com/assets/favicon-63b2904a073c89b52b19aa08cebc16a154bcf83fee8ecc6439968b1e6db569c7.ico" class="dribble"></li>
运行[class.member.lookup]算法,我们发现:
D::a
中没有声明a
,因此S(a,D)最初为空,我们在其基类中合并D
的查找集,计算方式如下:如下:
请注意,在S(a,B)的声明集中,成员为a
,即使它在A::a
中找到,对于S(a)也是如此,C):
在声明集中, using-declarations 被指定成员集[...]
取代
要确定成员访问权限B
是否含糊不清,我们现在检查 [expr.ref] / 5:
5 - 如果
命名类的模糊基础,则[[]]程序格式不正确d.a
直接成为成员的类是E2
[...]
此处E2
已被确定为E2
,A::a
的直接成员。命名类为A
。 D
不是A
的模糊基础,因为D
是A
的所有中间基类子对象的虚拟基础。因此D
在名称查找和成员访问中都是明确的,并且您的程序是正确的。
作为一个类似的实例,我们可以考虑用[class.member.lookup] / 9的注释中的静态成员替换虚拟继承:
9 - [注意:即使一个对象有多个基类子对象,也可以明确地找到基类
d.a
中定义的静态成员,嵌套类型或枚举数类型为T
。两个基类子对象共享其公共虚拟基类的非静态成员子对象。 - 结束说明]
T
这里我们再次得到S(a,D)= {{A :: a},{B,C}}。实际上,即使struct A { static int a; };
struct B : A { using A::a; };
struct C : A { using A::a; };
struct D : B, C { };
int main() {
D d;
d.a = 0; // OK
}
是非虚拟基础的非静态成员,名称查找也会以相同的方式进行; 名称查找是明确的,但成员访问 [expr.ref] 在这种情况下是不明确的。
为了进一步阐明名称查找和成员访问之间的区别,考虑即使对于非虚拟多重继承基类的非静态数据成员,也可以明确地使用获取派生类的成员的名称。作为命名类:
A::a
不幸的是,我试过的每个编译器都拒绝了这个,尽管这是(modulo using-declarations)an example in the Standard!