【问题标题】:Global scope resolution in the presence of using namespace存在使用命名空间时的全局范围解析
【发布时间】:2016-05-18 14:03:02
【问题描述】:

考虑以下代码:

namespace foo {
  namespace bar {
    class foo {};
  }
  class baz {};
}
using namespace foo::bar;

::foo::baz mybaz;

此代码有效吗? 还是::foo 模棱两可? 还是::foo指的是class foo,这样就没有::foo::baz了。

谈到编译器,gcc 6.1.1 似乎认为是后者:

scope.cpp:9:8: error: ‘baz’ in ‘class foo::bar::foo’ does not name a type
 ::foo::baz mybaz;
        ^~~

另一方面,gcc 5.3.1clang 3.8.0 和 intel 编译器 16.0.3 不会产生任何警告或错误。

我怀疑在 C++14 标准的 3.4.3.2.2 下,这应该是有效的并且没有歧义,但我不太确定。

编辑:此外,对于foo::baz mybaz,只有 clang 报告了一个模棱两可的错误。

【问题讨论】:

  • 有趣的是,foo::baz mybaz 也可以在 gcc 中编译。
  • 确实,foo::baz mybaz 只是说和clang 有歧义。

标签: c++ language-lawyer c++14 gcc6


【解决方案1】:

[namespace.qual] 相当简单:

1 如果 qualified-idnested-name-specifier 指定了一个命名空间(包括 nested-name-specifier 的情况>是::,即指定全局命名空间),在命名空间范围内查找nested-name-specifier之后指定的名称。 template-idtemplate-argument 中的名称在整个 postfix-expression 出现的上下文中查找。 p>

2 对于命名空间 X 和名称 m,命名空间限定的查找集 S(X,m) 定义如下:让 S0(@987654327 @,m) 是X 中所有m 声明的集合和X (7.3.1) 的内联命名空间集合。如果 S0(X,m) 不为空,则 S(X,m) 为 S0(X,@987654337 @);否则,对于由 using -directivesX 及其内联命名空间集中。

3 如果 S(X,m) 是空集,程序格式错误。否则,如果 S(X,m) 只有一个成员,或者如果引用的上下文是 using-declaration (7.3.3),则 S(X,@ 987654351@) 是m 的必需声明集。否则,如果 m 的使用不允许从 S(X,m) 中选择唯一声明,则程序是非良构的。

::foo 是一个 qualified-idnested-name-specifier ::,因此名称 foo 在全局范围内查找。 p>

S0(::,foo) 包含 namespace foo 的单个声明,因为命名空间中没有 foo 的其他声明,并且它没有内联命名空间。由于 S0(::,foo) 不为空,因此 S(::,foo) 为 S0(::,@987654368 @)。 (请注意,由于 S0(::,foo) 不为空,因此不会检查 using 指令指定的命名空间。)

由于 S(::,foo) 只有一个元素,因此该声明用于 ::foo

名称::foo::baz 因此是一个qualified-id,带有指定命名空间的nested-name-specifier ::foo。在命名空间::foo 中再次有一个baz 声明,因此名称::foo::baz 指的是class baz 声明。

您在 GCC 6+ 中观察到的行为实际上是一个错误,归档为 GCC PR 71173

编辑:当 foo::baz 出现在全局范围内时查找它,a la:

foo::baz bang;

首先需要查找foo 作为非限定名称。 [basic.lookup.qual]/1 说这个查找只看到“名称空间、类型和模板,其特化是类型。” [basic.scope.namespace]/1 告诉我们命名空间成员及其作用域:

namespace-definition 的声明区域是它的namespace-bodynamespace-body 中声明的实体被称为命名空间的成员,这些声明引入到命名空间的声明区域的名称被称为命名空间的成员名称。命名空间成员名称具有命名空间范围。它的潜在范围包括从名称声明点开始的名称空间;对于每个指定成员命名空间的using-directive,成员的潜在范围包括该成员声明点之后的using-directive 潜在范围部分。

[basic.lookup.unqual]/4 告诉我们namespace foo 的声明是可见的:

在全局范围内使用的名称,在任何函数、类或用户声明的命名空间之外,应在其在全局范围内使用之前声明。

第 2 段说 class foo 在这里也可见:

using-directive 指定的命名空间中的声明在包含 using-directive 的命名空间中变得可见;见 7.3.4。出于 3.4.1 中描述的非限定名称查找规则的目的,由 using-directive 指定的命名空间中的声明被视为该封闭命名空间的成员。

由于发现了来自不同命名空间的两个不同foo 实体的声明,[namespace.udir]/6 告诉我们foo 的使用是错误的:

如果名称查找在两个不同的命名空间中找到一个名称的声明,并且这些声明没有声明相同的实体且没有声明函数,则该名称的使用是错误的。

我们对foo::baz 的名称查找在到达baz 之前就消失了。

【讨论】:

  • 根据第2段我的回答似乎是错误的。您能否详细说明全局命名空间中的名称foo::baz?它无法在 clang 中编译,而 gcc 没有显示诊断。
  • 这确实与我的想法相似,但我仍在苦苦挣扎的是 [namespace.udir] 如何发挥作用,正如 user2079303 之前指出的那样。也感谢您报告错误。
  • @Zulan [namespace.udir]/6 开始“如果名称查找在两个不同的命名空间中找到一个名称的声明...”,因为查找 ::foo::foo::baz 只能找到单个命名空间中的单个声明,它不适用。
  • @user2079303 我有理由确定foo::baz 格式不正确,我在答案中添加了一些分析。 (不合格的查找规则使合格的名称查找看起来很简单 - 明白吗?“看起来”很简单? - 所以我不确定自己的立足点。)
猜你喜欢
  • 2012-05-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多