2010-08-23 50 views
7

下面是纯粹学术发明的类层次结构。预期的行为是什么?

struct X{ 
     void f1(); 
     void f2(); 
     void f3(); 
}; 

struct Y : private X{ 
     void f4(); 
}; 

struct Z : X{ 
}; 

struct D : Y, Z{ 
     using X::f2; 
     using Z::X::f3; 
}; 

int main(){} 

我预期使用声明X :: F2不明确为 'X' 是 'd'(visbility VS X的可接近)的一个不明确的基础。然而g ++(ideone.com)很好地编译它。

我使用Online Comeau进行了检查,发现X :: f2的声明出现错误。但是,它也为Z :: X :: f3使用声明提供了不明确的地方。

那么预期的行为是什么?

编辑1:

到标准的相应部分的引用将是有益的,请。

编辑2:

我与2010年VS检查,它只有与使用声明X :: F2异议。然而,这不是关于'X'的歧义(例如gcc和Comeau)。这是关于“错误C2876:'X':并非所有重载都可访问”。

编辑3:

struct X{ 
    void f(){} 
}; 

struct Y : X{ 
    struct trouble{ 
     void f(){} 
    }; 

}; 

struct trouble : X{ 
}; 

struct letscheck : Y, trouble{ 
    using trouble::f; 
}; 

int main(){} 

在这里,我试图(故意)与使用声明类型创建一个问题。海湾合作委员会仍然编译这个罚款,VS2010也是如此。 Comeau仍然给出了错误(如预期的)关于模棱两可的类型'麻烦'。按照初步查询的解释,看来GCC和VS2010是错误的。那是对的吗?

+0

在D添加一个方法,调用f2()看看会发生什么。 – 2010-08-23 15:00:18

回答

2

我不认为这些都是不合格的。首先,对于using X::f2,X被查找,并且这将毫无疑义地产生类型X。然后f2X被抬起,这也是明确的(它不在D查找!)。

第二种情况将出于同样的原因。

但是,如果你通话f2一个D对象上,呼叫将被暧昧,因为名称f2是在XD所有子对象抬头一看,D有两个这样的子对象,并f2是一种非静态成员函数。第二种情况同样如此。无论您是直接使用Z::X还是X来命名f3,这都没有什么不同。这两者都指定类别X

为了明确使用声明,您需要以不同的方式写入。请注意,在C++ 0x using ThisClass::...;无效。不过在C++ 03中,只要全名是指基类成员。相反,如果在C++ 0x中允许这样做,则整个使用声明也将是有效的,因为C++ 0x不会将子对象考虑在名称查找中:D::f2明确指的是只有一个声明X中的那个)。见DR #39和最终论文N1626

struct D : Y, Z{ 
    // ambiguous: f2 is declared in X, and X is a an ambiguous base class 
    using D::f2; 

    // still fine (if not referred to by calls/etc) :) 
    using Z::X::f3; 
}; 

struct E : D { 
    // ambiguous in C++03 
    // fine in C++0x (if not referred to by an object-context (such as a call)). 
    using D::f2; 
}; 

的C++标准03介绍了本段10.23.4.3.1。对于EDIT3


响应:

是,GCC和VS2010是错误的。 trouble是指通过注入的类名称::trouble找到的类型以及找到的嵌套类Y::trouble。名称trouble::之前使用非限定查找(由3.4.1/7,代表10.2在第一个项目符号中)忽略任何对象,函数和枚举器名称(3.4.3/1--在这种情况下不存在这样的名称)。然后,它违反了对10.2的要求,即:

If the resulting set of declarations are not all from sub-objects of the same type ... the program is ill-formed.


这可能是VS2010和GCC解释的C++ 0x的措辞不同于科莫和追溯执行这一措辞:

In a using-declaration used as a member-declaration, the nested-name-specifier shall name a base class of the class being defined.

这意味着考虑到非基类,但是如果命名了非基类,则它是错误的。如果标准打算忽略非基类的名字,那么只能在这里,或者明确地说出来(这两个练习都完成了)。然而,该标准完全不是由于其使用可以是。而GCC实现C++ 0x的措辞,因为它拒绝其他完全正确的C++ 03代码,只是因为using声明包含它的类名。

对于措辞不清楚的一个例子,考虑以下表达式:

a.~A(); 

这在语法上是不明确的,因为它可以是一个成员函数调用如果a是一个类对象,但它可以是一个伪 - 如果a具有标量类型(如int),则使用-destructor-call(这是不可操作的)。但是,什么标准说是一个伪析构函数调用和类成员访问的语法在5.2.45.2.5分别

The left-hand side of the dot operator shall be of scalar type.

For the first option (dot) the type of the first expression (the object expression) shall be “class object” (of a complete type).

这是错误的使用,因为它不清理的模糊性可言。它应该使用“只能”,编译器以这种方式解释它。这主要是历史原因,因为一些委员会成员最近在usenet上告诉我。见附件H的The rules for the structure and drafting of International Standards

+0

@litb:我修改了类“D”,如下所示:struct D:Y,Z {using X :: f2;使用Z :: X :: f3; void f(){X * p = this;}};现在我得到了错误prog.cpp:19:错误:重复使用声明'使用X :: f3'prog.cpp:在成员函数'void D :: f()':prog.cpp:20:error:'X '是'D'的模糊基础 '名称查找'期间'使用声明'的规则是否有所不同? – Chubsdad 2010-08-23 10:22:42

+0

@chubsdad我所能做的就是重复标准。我不知道这些编译器实现了什么。我在代码中看不到“重复使用声明”。 – 2010-08-23 10:24:58

+0

@litb:我的代码中有轻微的错字,因此编译器输出。修改后的错误如下: prog.cpp:在成员函数'void D :: f()'中: prog.cpp:20:错误:'X'是'D'的模糊基地 – Chubsdad 2010-08-23 10:28:25

0

使用X :: f2;不应该工作由于私有继承下面的代码

struct Y : private X{ 
    void f4(); 
}; 

它是不可能通过Y. 所以X :: F2将冲突访问X的成员。

Z::X::f2应该工作。 或Z::f2应该工作。

+0

C++具有多重访问规则。如果一个名称可以通过基类层次结构中的多个路径找到,并且其中一个名称是公共的,则会获取该公共访问路径。见'11.7/1'。 – 2010-08-23 10:08:28

+0

@litb:所以这意味着'使用X :: f2'是好的,因为'X'可以通过OP中的'Z'访问。但是,'X'是不明确的,因此模糊相关的错误。因此,有关'不是所有重载都可访问'的VS2010错误消息可能很棘手。这意味着Comeau是正确的。我至少怀疑有关使用“Z :: X :: f3”声明的错误。 – Chubsdad 2010-08-23 10:15:58

+0

@chubsdad它只是意味着它不是一个可访问性错误。它将访问权限更改为提供最多访问权限的路径。即使没有11.7/1,它也不是一个模糊的查询,但是我们不知道我们是否需要应用私有或公共可访问性。 – 2010-08-23 12:12:18

0

首先,澄清Johannes的答案。当你说using Z::X::f2;时,编译器不会“建立一条路径”到f2以跟踪应该如何访问它。由于Z::XX是一样的,所以声明与using X::f2;完全相同。这个例子对比吧:

struct A { void f() {} void g() {} }; 
struct B { void f() {} void g() {} }; 
struct C { typedef A X; }; 
struct D { typedef B X; }; 
struct E : A, B { 
    using C::X::f; // C::X == A 
    using D::X::g; // D::X == B 
}; 

Z::X工作不继承或会员的,而是因为该标识符X距离范围Z访问的语法。你甚至可以写出Z::Z::Z::Z::X::X::X::X令人厌恶,因为每个班级都将自己的名字带入自己的范围。因此::这里不表示继承。

现在,解决问题。 f2YZ继承自X。因此,它是的成员YZE不需要知道关于X,因为它是一个隐藏的实现细节。所以,你要

struct D : Y, Z{ 
    using Y::f2; // error: inaccessible 
    using Z::f3; 
}; 

要9.1/2条款,你问解释:

A class-name is inserted into the scope in which it is declared immediately after the class-name is seen. The class-name is also inserted into the scope of the class itself; this is known as the injected-class-name.

名称X注入XX::X。然后它被继承到YZYZ不会在其自己的范围内隐式声明X

10.2/2:

The following steps define the result of name lookup in a class scope, C. First, every declaration for the name in the class and in each of its base class sub-objects is considered. … If the resulting set of declarations are not all from sub-objects of the same type, or the set has a nonstatic member and includes members from distinct sub-objects, there is an ambiguity and the program is ill-formed. Otherwise that set is the result of the lookup.

注意我加粗的复数词子对象。尽管在两个子对象中找到名称X,但它们都是相同类型的,即X

+0

在'D'范围内,有两个'X'子对象(一个来自'Y'(无法访问),另一个来自' Z'(accessible))。参考$ 9.2(Injected class name)。不应该使用X :: f2与上述逻辑模糊不清,因为'X'是'D'的模糊基础? – Chubsdad 2010-08-23 11:04:05

+0

@chubs:只有一个名为'X'的类,''X'命名一个类,而不是一个子对象:这是我生病的地方ustration。 – Potatoswatter 2010-08-23 11:29:24

+0

@chubs:至于§9.1/ 2(不是$ 9.2),请参阅更新的答案。 – Potatoswatter 2010-08-23 11:37:11

相关问题