使用命名空间时的全局范围解析

Global scope resolution in the presence of using namespace

考虑以下代码:

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 和英特尔编译器 16.0.3 不会产生任何警告或错误。

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

编辑:此外,对于 foo::baz mybaz 只有 clang 报告不明确的错误。

[namespace.qual] 相当简单:

1 If the nested-name-specifier of a qualified-id nominates a namespace (including the case where the nested-name-specifier is ::, i.e., nominating the global namespace), the name specified after the nested-name-specifier is looked up in the scope of the namespace. The names in a template-argument of a template-id are looked up in the context in which the entire postfix-expression occurs.

2 For a namespace X and name m, the namespace-qualified lookup set S(X,m) is defined as follows: Let S0(X,m) be the set of all declarations of m in X and the inline namespace set of X (7.3.1). If S0(X,m) is not empty, S(X,m) is S0(X,m); otherwise, S(X,m) is the union of S(Ni,m) for all namespaces Ni nominated by using-directives in X and its inline namespace set.

3 Given X::m (where X is a user-declared namespace), or given ::m (where X is the global namespace), if S(X,m) is the empty set, the program is ill-formed. Otherwise, if S(X,m) has exactly one member, or if the context of the reference is a using-declaration (7.3.3), S(X,m) is the required set of declarations of m. Otherwise if the use of m is not one that allows a unique declaration to be chosen from S(X,m), the program is ill-formed.

::foo 是具有 nested-name-specifier ::qualified-id,因此名称foo 在全局范围内查找。

S0(::,foo) 包含 namespace foo 的单个声明,因为没有 [=47 的其他声明=] 在命名空间中,它没有内联命名空间。因为S0(::,foo)不为空,所以S(::,foo)是S0(::,foo)。 (请注意,由于 S0(::,foo) 不为空,因此从不检查通过 using 指令指定的命名空间。)

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

名称 ::foo::baz 因此是 qualified-id with 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 告诉我们有关命名空间成员及其范围的信息:

The declarative region of a namespace-definition is its namespace-body. Entities declared in a namespace-body are said to be members of the namespace, and names introduced by these declarations into the declarative region of the namespace are said to be member names of the namespace. A namespace member name has namespace scope. Its potential scope includes its namespace from the name’s point of declaration onwards; and for each using-directive that nominates the member’s namespace, the member’s potential scope includes that portion of the potential scope of the using-directive that follows the member’s point of declaration.

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

A name used in global scope, outside of any function, class or user-declared namespace, shall be declared before its use in global scope.

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

The declarations from the namespace nominated by a using-directive become visible in a namespace enclosing the using-directive; see 7.3.4. For the purpose of the unqualified name lookup rules described in 3.4.1, the declarations from the namespace nominated by the using-directive are considered members of that enclosing namespace.

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

If name lookup finds a declaration for a name in two different namespaces, and the declarations do not declare the same entity and do not declare functions, the use of the name is ill-formed.

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