预期的行为是什么?
下面是纯粹学术上发明的阶级层次结构。
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”的不明确基数(X 的可见性与可访问性)。然而 g++ (ideone.com) 可以很好地编译它。
我检查了 Online Comeau,它在使用 X::f2 的声明时出现了预期的错误。然而,它也给使用 Z::X::f3 的声明带来了歧义。
那么预期的行为是什么?
编辑 1:
请参考该标准的相应部分。
编辑2:
我检查了VS 2010,它仅对using声明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(){}
在这里,我尝试(有目的地)在 using 声明中创建类型问题。 Gcc 仍然可以很好地编译,VS2010 也是如此。 Comeau 仍然给出关于不明确类型“麻烦”的错误(如预期)。根据最初查询的解释,GCC 和 VS2010 似乎是错误的。这是正确的吗?
Below is a purely academically invented class hierarchy.
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(){}
I expected using declaration for X::f2 to be ambiguous as 'X' is an ambiguous base of 'D' (visbility vs accessibility of X). However g++ (ideone.com) compiles it fine.
I checked with Online Comeau and it gives error in using declaration for X::f2 as expected. However it gives ambiguity for using declaration for Z::X::f3 as well.
So what is the expected behavior?
Edit 1:
A reference to the appropriate section of the Standard would be helpful, please.
Edit 2:
I checked with VS 2010 and it has objections only with the using declaration X::f2. However it is not about ambiguity of 'X' (as in the case of gcc and Comeau). It is about "error C2876: 'X' : not all overloads are accessible".
Edit 3:
struct X{
void f(){}
};
struct Y : X{
struct trouble{
void f(){}
};
};
struct trouble : X{
};
struct letscheck : Y, trouble{
using trouble::f;
};
int main(){}
Here I have attempted (purposefully) to create an issue with types in using declaration. Gcc still compiles this fine and so does VS2010. Comeau still gives error (as expected) about ambiguous types 'trouble'. Going by explanations given for the initial queries, it appears GCC and VS2010 are wrong. Is that correct?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我不认为这些都是不规范的。首先,对于
使用X::f2
,会查找X
,这将明确产生类类型X
。然后查找X
中的f2
,这也是明确的(它不会在D
中查找!)。出于同样的原因,第二种情况也会起作用。
但是,如果您在
D
对象上调用f2
,则调用将是不明确的,因为名称f2
被查找D
的X
类型的所有子对象中都存在,并且D
有两个这样的子对象,并且f2
是一个非-静态成员函数。同样的原因也适用于第二种情况。无论您直接使用Z::X
还是X
命名f3
,都没有什么区别。这两个都指定类X
。为了使 using 声明具有歧义,您需要以不同的方式编写它。请注意,在 C++0x 中
using ThisClass::...;
无效。但在 C++03 中,只要整个名称引用基类成员即可。相反,如果 C++0x 中允许这样做,则整个 using 声明也将有效,因为 C++0x 不会明确地考虑子对象进行名称查找:
D::f2
仅指一个声明(X
中的声明)。请参阅 DR #39 和最终论文N1626。C++03 标准在
10.2
和3.4.3.1
段落中对此进行了描述。Edit3的回应:
是的,GCC和VS2010是错误的。
trouble
指的是通过注入的类名::trouble
找到的类型以及以Y::trouble
形式找到的嵌套类。::
之前的名称trouble
使用非限定查找进行查找(通过3.4.1/7
,委托给10.2< /code> 在第一个项目符号中)忽略任何对象、函数和枚举器名称(
3.4.3/1
- 但在本例中没有这样的名称)。它违反了10.2
的要求:VS2010 和 GCC 对 C++0x 措辞的解释可能与 Comeau 不同,并追溯实现该措辞:
这意味着会考虑非基类,但如果命名非基类,则会出现错误。如果标准打算忽略非基类名称,它会说只能在这里,或者明确地拼写出来(两种做法都已完成)。然而,该标准并不因使用应和可以而产生。 GCC 实现了 C++0x 措辞,因为它拒绝其他完全良好的 C++03 代码,只是因为 using 声明包含其类名。
对于措辞不明确的示例,请考虑以下表达式:
这在语法上是不明确的,因为如果
a
是类对象,则它可以是成员函数调用,但它可以是伪析构函数调用(这是一个无操作)如果a
具有标量类型(例如int
)。但标准所说的是分别在5.2.4
和5.2.5
处的伪析构函数调用和类成员访问的语法这是错误的用法,因为它根本无法消除歧义。它应该使用“can only”,并且编译器以这种方式解释它。正如一些委员会成员最近在新闻组上告诉我的那样,这主要是历史原因。请参见国际标准的结构和起草规则,附件 H。
I don't think that any of these are ill-formed. First, for
using X::f2
,X
is looked up, and this will unambiguously yield the class typeX
. Thenf2
inX
is looked up, and this is unambiguous too (it is not looked up inD
!).The second case will work for the same reason.
But if you call
f2
on aD
object, the call will be be ambiguous because the namef2
is looked up in all subobjects ofD
of typeX
, andD
has two such subobjects, andf2
is a non-static member function. The same reason holds for the second case. It does not make a difference for this whether you namef3
usingZ::X
orX
directly. Both of these designate the classX
.To get an ambiguity for the using declaration, you need to write it differently. Note that in C++0x
using ThisClass::...;
is not valid. It is in C++03 though, as long as the whole name refers to a base-class member.Conversely, if this would be allowed in C++0x, the whole using declaration would also be valid, because C++0x does not take subobjects into account for name-lookup:
D::f2
unambiguously refers to only one declaration (the one inX
). See DR #39 and the final paper N1626.The C++03 Standard describes this in paragraphs
10.2
and3.4.3.1
.Response for Edit3:
Yes, GCC and VS2010 are wrong.
trouble
refers to the type found by the injected class name of::trouble
and to the nested class found asY::trouble
. The nametrouble
preceeding the::
is looked up using unqualified lookup (by3.4.1/7
, which delegates to10.2
in the first bullet) ignoring any object, function and enumerator names (3.4.3/1
- there are no such names in this case, though). It then violates against10.2
's requirement that:It is possible that VS2010 and GCC interpret C++0x wording differently than Comeau and retroactively implement that wording:
This means that non-base classes are considered, but it is an error if a non-base class is named. If the Standard would intend to ignore non-base class names, it would say can only here, or spell it out explicitly (both practices are done). The Standard however is not at all consequent with its use of shall and can. And GCC implements C++0x wording, because it rejects otherwise completely fine C++03 code, just because the using declaration contains its class-name.
For an example of the unclear wording, consider the following expression:
This is syntactically ambiguous, because it can be a member function call if
a
is a class object, but it can be a pseudo-destructor-call (which is a no-op) ifa
has a scalar type (such asint
). But what the Standard says is for the syntax of a pseudo-destructor call and class member access at5.2.4
and5.2.5
respectivelyThat is the wrong use, because it does not clear up the ambiguity at all. It should use "can only", and compilers interpret it in that way. This has mostly historical reasons, as some committee-member recently told me on usenet. See The rules for the structure and drafting of International Standards, Annex H.
使用 X::f2;由于以下代码的私有继承,不应该工作
不可能通过 Y 访问 X 的成员。
所以 X::f2 会发生冲突。
Z::X::f2
应该可以工作。或者
Z::f2
应该可以工作。using X::f2; should not work due to private inheritance of below code
It is not possible to access members of X through Y.
So X::f2 would conflicts.
Z::X::f2
should work.Or
Z::f2
should work.首先,澄清约翰内斯的答案。当您说
using Z::X::f2;
时,编译器不会“构建到f2
的路径”来跟踪应如何访问它。由于Z::X
与X
相同,因此声明与using X::f2;
完全相同。与此示例进行对比:语法
Z::X
之所以有效,不是因为继承或成员身份,而是因为标识符X
可以从范围Z
访问代码>.你甚至可以写出让人恶心的Z::Z::Z::Z::X::X::X::X
,因为每个类都将自己的名称带入自己的作用域中。因此这里的::
并不表示继承。现在,来解决问题。
f2
由Y
和Z
从X
继承。因此,它是Y
和Z
的一流成员。E
不需要了解X
,因为它是隐藏的实现细节。所以,你想按照你的要求用 9.1/2 来解释:
名称
X
作为X::X
注入到X
中。然后它被继承到Y
和Z
中。Y
和Z
不会在自己的作用域中隐式声明X
。10.2/2:
请注意,我将复数单词子对象加粗。虽然名称
X
出现在两个子对象中,但它们都是相同的类型,即X
。First, to clarify Johannes' answer. When you say
using Z::X::f2;
, the compiler does not "build a path" tof2
to keep track of how it should be accessed. SinceZ::X
is the same thing asX
, the declaration is exactly the same as sayingusing X::f2;
. Contrast it with this example:The syntax
Z::X
works not because of inheritance or membership, but because the identifierX
is accessible from the scopeZ
. You are even allowed to writeZ::Z::Z::Z::X::X::X::X
ad nauseam, because every class brings its own name into its own scope. Thus::
here does not express inheritance.Now, to solve the problem.
f2
is inherited byY
andZ
fromX
. Thus, it is a first-class member ofY
andZ
.E
doesn't need to know aboutX
because it is a hidden implementation detail. So, you wantTo explain in terms of 9.1/2 as you ask:
The name
X
is injected intoX
asX::X
. It is then inherited intoY
andZ
.Y
andZ
do not implicitly declareX
in their own scope.10.2/2:
Note that I bolded the plural word sub-objects. Although the name
X
is found in two sub-objects, they are both the same type, namelyX
.