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

Zul*_*lan 13 c++ language-lawyer c++14 gcc6

请考虑以下代码:

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

::foo::baz mybaz;
Run Code Online (Sandbox Code Playgroud)

这段代码有效吗?还是::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;
        ^~~
Run Code Online (Sandbox Code Playgroud)

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

我怀疑在C++ 14标准的3.4.3.2.2下,这应该是有效的而不是含糊不清的,但我不太确定.

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

Cas*_*sey 9

[namespace.qual]非常简单:

1如果嵌套名称说明符一个的合格-ID提名一个命名空间(包括其中的情况下嵌套的名称说明符::,即提名的全局命名空间),后指定的名称嵌套名称说明符的抬头在命名空间的范围内.在整个后缀表达式发生的上下文中查找template-idtemplate-argument中的名称.

2对于名称空间X和名称m,名称空间限定的查找集S(X,m)定义如下:设S 0(X,m)是min 的所有声明的X集合,以及X(7.3.1)的内联命名空间集合.如果S 0(X,m)不为空,则S(X,m)为S 0(X,m); 否则,S( ,X)m是S的并集(N ,m)的所有名称空间Ñ 提名using指令X其内联的命名空间集合.

给定X::m(在哪里X是用户声明的命名空间)或给定::m(在哪里X是全局命名空间),如果S(X,m)是空集,则程序格式错误.否则,如果S(X,m)只有一个成员,或者引用的上下文是using-declaration(7.3.3),则S(X,m)是必需的声明集m.否则,如果使用的m不是允许从S(X,m)中选择唯一声明的那个,则程序是不正确的.

::foo是具有嵌套名称说明符qualified-id,因此在全局范围内查找名称. ::foo

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

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

::foo::baz因此,该名称是带有nested-name-specifierqualified-id,用于指定命名空间.在命名空间中还有一个声明,因此名称引用声明. ::foobaz::foo::foo::bazclass baz

您在GCC 6+中观察到的行为实际上是一个错误,以GCC PR 71173的形式提交.

编辑:查看foo::baz它何时出现在全球范围内,la:

foo::baz bang;
Run Code Online (Sandbox Code Playgroud)

首先需要查找foo作为非限定名称.[basic.lookup.qual]/1表示此查找只能看到"其特化是类型的名称空间,类型和模板".[basic.scope.namespace]/1告诉我们命名空间成员及其范围:

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

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

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

和第2段说class foo这里也可以看到:

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

由于不同的两个声明foo从不同的命名空间实体发现,[namespace.udir]/6告诉我们使用的foo是形成不良的:

如果名称查找在两个不同的名称空间中找到名称的声明,并且声明不声明相同的实体并且不声明函数,则名称的使用是错误的.

我们的名字foo::baz在去之前查找死亡baz.