13

考虑以下代码:

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 报告了一个模棱两可的错误。

4

1 回答 1

9

[namespace.qual] 相当简单:

1 如果qualified-id的nested-name-specifier指定了一个命名空间(包括nested-name-specifier为的情况,即指定全局命名空间),查找nested-name-specifier后面指定的名称在命名空间范围内。在整个后缀表达式出现的上下文中查找模板 ID的模板参数中的名称。::

2 对于命名空间X和名称m,命名空间限定的查找集 S( X, m) 定义如下:令 S 0 ( X, ) 是inm的所有声明和(7.3.1)的内联命名空间集的集合。若 S 0 ( , ) 不为空,则 S( , ) 为 S 0 ( , );否则,对于使用指令in指定的所有命名空间 N i及其内联命名空间集,S( , ) 是 S(N i , ) 的并集。mXXXmXmXmXmmX

3 给定X::m(whereX是用户声明的命名空间)或给定::m(whereX是全局命名空间),如果 S( X, m) 是空集,则程序是非良构的。否则,如果 S( X, m) 恰好只有一个成员,或者如果引用的上下文是using 声明(7.3.3),则 S( X, m) 是所需的声明集m。否则,如果使用的m不是一个允许从 S( X, m) 中选择唯一声明的方法,则程序是非良构的。

::foo是带有嵌套名称说明符的限定 ID,因此在全局范围内查找名称。 ::foo

S 0 ( ::, foo) 包含单个声明 fornamespace foo因为在命名空间中没有其他声明foo,并且它没有内联命名空间。由于 S 0 ( ::, foo) 不为空,所以 S( ::, foo) 为 S 0 ( ::, foo)。(请注意,由于 S 0 ( ::, foo) 不为空,因此永远不会检查使用指令指定的命名空间。)

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

因此,该名称::foo::baz是一个带有嵌套名称说明符的限定 ID ,它指定了一个命名空间。在命名空间中再次有一个声明,因此名称指的是声明。 ::foobaz::foo::foo::bazclass baz

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

编辑:查找foo::baz它出现在全局范围内的时间,例如:

foo::baz bang;

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

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

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

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

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

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

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

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

我们的名字查找foo::bazdie before ever get to baz

于 2016-05-18T14:50:27.003 回答