C ++中成员名称LookUp和Access声明中的歧义

时间:2017-06-19 09:08:50

标签: c++ c++11 c++14 member c++03

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;
}        

第一组类声明(ABCD)以及第二组(WXYZ)的构建类似,只是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时出现歧义的原因是什么?

据我所知,类BC中的访问声明都指向相同的基类成员。顺便说一句,如果我删除它们,测试会成功编译,因为a已经可公开访问(public访问说明符)。

提前致谢。

注意:以上代码是来自SolidSands'的稍加修改的测试。 SuperTest套件。

1 个答案:

答案 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},{B}}
    • S(a,C)= {{A :: a},{C}}
  • 生成的查找集为S(a,D)= {{A :: a},{B,C}}

请注意,在S(a,B)的声明集中,成员为a,即使它在A::a中找到,对于S(a)也是如此,C):

  

在声明集中, using-declarations 被指定成员集[...]

取代

要确定成员访问权限B是否含糊不清,我们现在检查 [expr.ref] / 5:

  

5 - 如果d.a直接成为成员的类是E2 [...]

命名类的模糊基础,则[[]]程序格式不正确

此处E2已被确定为E2A::a的直接成员。命名类为AD不是A的模糊基础,因为DA的所有中间基类子对象的虚拟基础。因此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