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

时间:2016-05-18 14:03:02

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

请考虑以下代码:

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报告的模糊错误。

1 个答案:

答案 0 :(得分:9)

[namespace.qual]非常简单:

  

1如果 qualified-id 嵌套名称说明符指定名称空间(包括 nested-name-specifier is ::,即,指定全局命名空间),在命名空间的范围内查找 nested-name-specifier 之后指定的名称。 template-id template-argument 中的名称将在整个 postfix-expression 出现的上下文中查找。

     

2对于名称空间X和名称m,名称空间限定的查找集S(Xm)定义如下:设S 0 Xm)是mX的所有声明的集合,X的内联命名空间集(7.3。 1)。如果S 0 Xm)不为空,则S(Xm)为S 0 Xm);否则,S(Xm)是所有名称空间N i 的S(N i m)的并集由X中的 using-directives 及其内联命名空间集提名。

     

3给定X::m(其中X是用户声明的命名空间)或给定::m(其中X是全局命名空间),如果是S({{ 1}},X)是空集,程序格式错误。否则,如果S(mX)只有一个成员,或者引用的上下文是 using-declaration (7.3.3),则S({ {1}},m)是X所需的声明集。否则,如果使用m不允许从S(mm)中选择唯一声明,则该程序格式不正确。

X qualified-id ,其中包含嵌套名称说明符 m,因此查找名称::foo在全球范围内。

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

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

因此,名称foo qualified-id ,其中 nested-name-specifier ::foo指定了名称空间。在命名空间::foo::baz中再次声明::foo,因此名称baz引用::foo声明。

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

编辑:::foo::baz在全局范围内查找,la:

class baz

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

  

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

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

  

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

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

  

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

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

  

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

我们foo的名字查询在到达foo之前就已经死了。