使用声明链接的程序在 MSVS 和 clang 上编译,但在 GCC 上不编译

Program with chaining of using-declarations compiles on MSVS and clang but not on GCC

根据 c++ 标准,下面的程序是良构还是病构的?

namespace X { int i; }

namespace Y { using X::i; }

int main() { using X::i; using Y::i; }

我使用不同的编译器得到不同的结果:

我不想修复此程序以使其在 GCC 上编译。我只想知道 c++ 标准对此有何规定,以及为什么三个编译器的行为不同。如果这是任何这些编译器中的错误的结果,我也想知道。

该程序不应编译,因为它在同一块范围内声明了两次 X::i

C++14 §7.3.3/10:

A using-declaration is a declaration and can therefore be used repeatedly where (and only where) multiple declarations are allowed. [Example:

namespace A {
    int i;
}

namespace A1 {
    using A::i;
    using A::i;           // OK: double declaration
}

void f() {
    using A::i;
    using A::i;           // error: double declaration
}

编辑: 上面引用的 非规范 评论,我认为它回答了问题,最初是在 C++ 中吗98,并通过技术勘误表 1 (C++03)、C++11 和 C++14 幸存下来。但显然错误。 Richard Smith 在他的回答中引用了 core issue 36,Andrew Koenig 于 1998 年 8 月 2nd 首次提出(在 ANSI 批准第一个标准后不到一个月),这显然意味着已知的错误评论可以在标准的三次修订后继续存在。

引用核心问题本身:

C++ 标准核心语言活跃问题,第 36 期:

Notes from 04/00 meeting:
The core language working group was unable to come to consensus over what kind of declaration a using-declaration should emulate. In a straw poll, 7 members favored allowing using-declarations wherever a non-definition declaration could appear, while 4 preferred to allow multiple using-eclarations only in namespace scope (the rationale being that the permission for multiple using-declarations is primarily to support its use in multiple header files, which are seldom included anywhere other than namespace scope). John Spicer pointed out that friend declarations can appear multiple times in class scope and asked if using-declarations would have the same property under the "like a declaration" resolution.

As a result of the lack of agreement, the issue was returned to "open" status.

C++98 和 C++14 的§3.3.1/4 中对多个同名声明的一般性讨论。据我所知,C++14 文本与原始 C++98 文本逐字相同。在许多情况下,它本身允许在同一个声明区域中多次声明相同的名称,其中之一是所有声明都引用同一个实体:

C++14 §3.3.1/4:

Given a set of declarations in a single declarative region, each of which specifies the same unqualified name,

  • they shall all refer to the same entity, or all refer to functions and function templates; or

  • exactly one declaration shall declare a class name or enumeration name that is not a typedef name and the other declarations shall all refer to the same variable or enumerator, or all refer to functions and function templates; in this case the class name or enumeration name is hidden (3.3.10). [Note: A namespace name or a class template name must be unique in its declarative region (7.3.2, Clause 14). —end note]

不过,这里的写法只是说什么,并不是直接无效。一个声明可以被其他规则禁止,即使它没有被这个规则禁止。例如class成员声明有这样的限制:

C++14 §9.2/1:

[…] A member shall not be declared twice in the member- specification, except that a nested class or member class template can be declared and then later defined, and except that an enumeration can be introduced with an opaque-enum-declaration and later redeclared with an enum-specifier.

我找不到这样的限制来支持上面开头引用的 C++14 §7.3.3/10 中明显不正确的注释,即我找不到对块作用域或名称空间作用域的任何特殊处理,因此,一个初步的结论(尽管在 1998 年已经受到质疑,但请记住该评论仍然存在)是,有争议的评论实际上是错误的,并且这个问题的代码(同一声明区域中的两个声明指的是同一实体)是有效并应被所有编译器接受。

Clang 和 MSVC 是正确的;此代码有效。正如 Alf 所说,[namespace.udecl] (7.3.3)/10

A using-declaration is a declaration and can therefore be used repeatedly where (and only where) multiple declarations are allowed.

但是,在块范围内对同一实体的多次声明没有限制,因此原始示例有效。不涉及 using-declarations 的相应情况是:

int n;
void f() {
  extern int n;
  extern int n;
}

这是有效的(并且被 GCC、EDG、Clang 和 MSVC 接受),因此(根据上面引用的规则)原始示例也是有效的。

值得注意的是[namespace.udecl](7.3.3)/10中的例子有错误。它说:

namespace A {
  int i;
}

void f() {
  using A::i;
  using A::i; // error: double declaration
}

...但是注释不正确;第二次声明没有错误。参见标准core issue 36. I've removed the example中的讨论,以免混淆更多人。