19

根据 c++ 标准,以下程序是格式正确的还是格式错误的?

namespace X { int i; }

namespace Y { using X::i; }

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

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

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

4

2 回答 2

12

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

C++14 §7.3.3/10:

using-declaration 是一种声明,因此可以在允许(且仅在)允许多个声明的情况下重复使用。[示例:

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 在他的回答中引用了关于它的核心问题 36,首先由 Andrew Koenig 于 1998 年 8 月 2提出(在 ANSI 批准第一个标准后不到一个月),这显然意味着一个已知的不正确评论可以在标准。

引用核心问题本身:

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

04/00 会议记录:核心语言工作组无法就using-declaration应仿效
哪种声明达成共识。在一次草率投票中,7 名成员赞成在任何可能出现非定义声明的地方允许使用声明,而 4 名成员更倾向于仅在命名空间范围内允许多个using-eclarations(理由是允许多个using-declarations主要是为了支持它在多个头文件中的使用,这些头文件很少包含在命名空间范围以外的任何地方)。John Spicer 指出声明可以在类范围内出现多次,并询问是否使用声明friend在“类似声明”决议下将具有相同的属性。

由于缺乏协议,该问题被恢复为“开放”状态。

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

C++14 §3.3.1/4:

给定单个声明区域中的一组声明,每个声明都指定相同的非限定名称,

  • 都指同一个实体,或者都指函数和函数模板;或者

  • 恰好一个声明应声明一个不是 typedef 名称的类名或枚举名,而其他声明应全部引用相同的变量或枚举数,或全部引用函数和函数模板;在这种情况下,类名或枚举名是隐藏的(3.3.10)。[注意:命名空间名称或类模板名称在其声明区域中必须是唯一的(7.3.2,第 14 条)。——尾注]

但是,这里的措辞只说明了不直接无效的内容。一个声明可以被其他规则禁止,即使它没有被这条规则禁止。例如,对类成员声明有这样的限制:

C++14 §9.2/1:

[...] 成员不得在成员规范中声明两次,除非可以声明嵌套类或成员类模板,然后再定义,并且可以使用opaque-enum-declaration和后来用enum-specifier重新声明。

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

于 2015-07-04T20:49:25.373 回答
6

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

using-declaration是一种声明因此可以在允许(并且仅在)允许多个声明的地方重复使用。

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

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
}

...但评论不正确;第二个声明没有错误。参见核心问题 36中的讨论。我已从标准中删除了该示例,以免使更多人感到困惑。

于 2015-07-05T20:16:02.297 回答