预期的行为是什么?

发布于 2024-09-15 05:09:04 字数 1021 浏览 9 评论 0原文

下面是纯粹学术上发明的阶级层次结构。

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

情绪少女 2024-09-22 05:09:04

我不认为这些都是不规范的。首先,对于使用X::f2,会查找X,这将明确产生类类型X。然后查找X中的f2,这也是明确的(它不会在D中查找!)。

出于同样的原因,第二种情况也会起作用。

但是,如果您在 D 对象上调用 f2,则调用将是不明确的,因为名称 f2 被查找DX 类型的所有子对象中都存在,并且 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

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< /code> 在第一个项目符号中)忽略任何对象、函数和枚举器名称(3.4.3/1 - 但在本例中没有这样的名称)。它违反了 10.2 的要求:

如果生成的声明集并非全部来自同一类型的子对象...则程序格式错误。


VS2010 和 GCC 对 C++0x 措辞的解释可能与 Comeau 不同,并追溯实现该措辞:

在用作成员声明的 using 声明中,嵌套名称说明符应命名所定义类的基类。

这意味着会考虑非基类,但如果命名非基类,则会出现错误。如果标准打算忽略非基类名称,它会说只能在这里,或者明确地拼写出来(两种做法都已完成)。然而,该标准并不因使用可以而产生。 GCC 实现了 C++0x 措辞,因为它拒绝其他完全良好的 C++03 代码,只是因为 using 声明包含其类名。

对于措辞不明确的示例,请考虑以下表达式:

a.~A();

这在语法上是不明确的,因为如果 a 是类对象,则它可以是成员函数调用,但它可以是伪析构函数调用(这是一个无操作)如果 a 具有标量类型(例如 int)。但标准所说的是分别在 5.2.45.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 type X. Then f2 in X is looked up, and this is unambiguous too (it is not looked up in D!).

The second case will work for the same reason.

But if you call f2 on a D object, the call will be be ambiguous because the name f2 is looked up in all subobjects of D of type X, and D has two such subobjects, and f2 is a non-static member function. The same reason holds for the second case. It does not make a difference for this whether you name f3 using Z::X or X directly. Both of these designate the class X.

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 in X). See DR #39 and the final paper 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;
};

The C++03 Standard describes this in paragraphs 10.2 and 3.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 as Y::trouble. The name trouble preceeding the :: is looked up using unqualified lookup (by 3.4.1/7, which delegates to 10.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 against 10.2's requirement that:

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


It is possible that VS2010 and GCC interpret C++0x wording differently than Comeau and retroactively implement that wording:

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

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:

a.~A();

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) if a has a scalar type (such as int). But what the Standard says is for the syntax of a pseudo-destructor call and class member access at 5.2.4 and 5.2.5 respectively

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).

That 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.

将军与妓 2024-09-22 05:09:04

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

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

不可能通过 Y 访问 X 的成员。
所以 X::f2 会发生冲突。

Z::X::f2 应该可以工作。
或者 Z::f2 应该可以工作。

using X::f2; should not work due to private inheritance of below code

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

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.

爱冒险 2024-09-22 05:09:04

首先,澄清约翰内斯的答案。当您说 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 ,因为每个类都将自己的名称带入自己的作用域中。因此这里的 :: 并不表示继承。

现在,来解决问题。 f2YZX 继承。因此,它是 YZ 的一流成员E 不需要了解 X,因为它是隐藏的实现细节。所以,你想

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

按照你的要求用 9.1/2 来解释:

类名被插入到
声明的范围
紧接在类名之后
看到了。类名也被插入
进入类本身的范围;
这被称为
注入类名。

名称 X 作为 X::X 注入到 X 中。然后它被继承到 YZ 中。 YZ 不会在自己的作用域中隐式声明 X

10.2/2:

以下步骤定义类中名称查找的结果
范围,C。首先,每个声明
班级和每个班级中的姓名
它的基类子对象是
经过考虑的。 ……
如果生成的声明集
并非全部来自子对象
相同类型,或者该集合有一个
非静态成员和包含成员
从不同的子对象中,有一个
歧义,程序是
格式不正确。否则该集合就是
查找结果。

请注意,我将复数单词子对象加粗。虽然名称X出现在两个子对象中,但它们都是相同的类型,即X

First, to clarify Johannes' answer. When you say using Z::X::f2;, the compiler does not "build a path" to f2 to keep track of how it should be accessed. Since Z::X is the same thing as X, the declaration is exactly the same as saying using X::f2;. Contrast it with this example:

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

The syntax Z::X works not because of inheritance or membership, but because the identifier X is accessible from the scope Z. You are even allowed to write Z::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 by Y and Z from X. Thus, it is a first-class member of Y and Z. E doesn't need to know about X because it is a hidden implementation detail. So, you want

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

To explain in terms of 9.1/2 as you ask:

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.

The name X is injected into X as X::X. It is then inherited into Y and Z. Y and Z do not implicitly declare X in their own scope.

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.

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, namely X.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文