当非限定名称查找涉及 using 指令时对 [basic.scope.hiding]p2 的解释

Interpretation of [basic.scope.hiding]p2 when unqualified name lookup involves using-directives

提问人:Supremum 提问时间:7/29/2015 更新时间:7/29/2015 访问量:125

问:

在 c++ 中隐藏了两种类型的名称:

1) 正常名称隐藏:[basic.scope.hiding]p1 (http://eel.is/c++draft/basic.scope.hiding#1):

名称可以通过在 嵌套的声明性区域或派生类 ([class.member.lookup])。

2) 隐藏在 [basic.scope.hiding]p2 (http://eel.is/c++draft/basic.scope.hiding#2) 中的特殊名称类型:

类名 ([class.name]) 或枚举名 ([dcl.enum]) 可以是 被变量、数据成员、函数或枚举器的名称隐藏 在同一范围内声明。如果类或枚举名称和 变量、数据成员、函数或枚举器在 具有相同名称、类或枚举的相同作用域(按任何顺序) 名称隐藏在变量、数据成员、函数或 枚举器名称可见。

我很想知道在执行非限定名称查找时,名称隐藏如何与 using 指令交互。

对于第一种类型的名称,隐藏行为是很清楚的。这是因为 [basic.scope.hiding]p1 已根据 [basic.lookup.unqual] (http://eel.is/c++draft/basic.lookup.unqual 部分中的规则进行了重新表述)

对于第二种类型的名称隐藏,没有这样做。所以现在出现了以下问题:

*) 第二种类型的名称隐藏应如何与涉及 using 指令的非限定名称查找交互?

在标准中查找其他位置,我找到了 [namespace.udir]p2 (http://eel.is/c++draft/namespace.udir#2),我认为这是回答这个问题的关键:

using 指令指定指定命名空间中的名称 可以在 using 指令出现在 之后的作用域中使用 using 指令。在非限定名称查找期间 ([basic.lookup.unqual]),这些名称看起来就像是在最近封闭的命名空间中声明的一样,该命名空间包含 using-指令和指定的命名空间。[注:在此背景下, “包含”是指“直接或间接包含”。——尾注 ]

将此规则的 as 部分应用于 [basic.scope.hiding]p1 可与 [basic.lookup.unqual] 部分中的规则保持一致。此应用程序也与 [basic.scope.hiding]p4 (http://eel.is/c++draft/basic.scope.hiding#4) 一致,所以这看起来很有希望。

正因为如此,我认为我们可以通过类似地将 [namespace.udir]p2 的 as 部分应用于 [basic.scope.hiding]p2 来回答这个问题 *)。此应用程序也与 [basic.scope.hiding]p4 一致。我认为这也是对 c++ 标准最自然、最不复杂的解释。

然而,问题在于 Clang 和 GCC 的解释与我不同。例如:

namespace N { static int i = 1; }
namespace M { struct i {}; }
using namespace M;
using namespace N;    
int main() { sizeof(i); }

根据我的解释,这个程序应该格式良好,应该作为整数变量进行查找。Clang 和 GCC 都不同意这一点,因为他们给出了一个名称查找歧义。i

在 Clang 的情况下,这种更复杂的解释会导致以下错误:

namespace N { static int i = 1; }
namespace M { struct i {}; }
namespace P {
    using N::i;
    using M::i;
}
namespace Q { using M::i; }
using namespace P;
using namespace Q;
int main() { sizeof (i); }

没有错误,但会改变

using namespace P;
using namespace Q;

using namespace Q;
using namespace P;

我们得到名称查找歧义错误。GCC 在这里至少是一致的。

我是否正确解释了 c++ 标准?

C++ 语言律师 using-directives 名称查找

评论


答:

2赞 Columbo 7/29/2015 #1
namespace N { static int i = 1; }
namespace M { struct i {}; }
using namespace M;
using namespace N;    
int main() { sizeof(i); }

这是不正确的。§7.3.4/6:

如果名称查找在两个不同的名称中查找名称的声明 命名空间,并且声明不声明相同的实体,并且声明 不声明函数,则名称的使用格式不正确。

这同样适用于您的第二个示例。请注意,适用于例如

struct A {} A;

...不适用于您的情况,因为这两个 S 是在不同的范围内声明的。也i

在非限定名称查找 ([basic.lookup.unqual]) 期间,名称 看起来好像它们是在最近的封闭命名空间中声明的 它包含 using 指令和指定的命名空间。

也无关紧要,因为名称查找产生的任何歧义,如您的示例中 ,都是在查找后处理的 - 例如前面提到的 §7.3.4/6。i

3赞 Barry 7/29/2015 #2

我认为这里的关键词是:

名称可以通过嵌套声明性区域中相同名称的显式声明来隐藏,也可以派生 类 (10.2)。

类名 (9.1) 或枚举名 (7.2) 可以通过变量、数据成员、 函数或在同一作用域中声明的枚举器。

在此示例中:

namespace N { static int i = 1; }
namespace M { struct i {}; }
using namespace M;
using namespace N;    
int main() { sizeof(i); }

这两个 s 都在不同的非嵌套作用域中声明,因此没有隐藏。名称查找会找到它们,就好像它们是在 中声明的一样但这不是隐藏规则规定的。i::

否则,我们从 [basic.lookup] 中:

名称查找应 为名称找到一个明确的声明(见 10.2)。名称查找可以关联多个声明 如果发现该名称是函数名称,则为名称;

中没有明确的声明,因此此代码格式不正确,错误是正确的。另一个例子也是如此,所以有一些 clang 编译它的 using 声明顺序是一个错误。::

虽然这是非规范性的,但 [namespace.udir] 中有一个示例清楚地表明了这种解释:

[ 注:特别是, 变量、函数或枚举器的名称不会隐藏声明的类或枚举的名称 在不同的命名空间中。例如

namespace A {
    class X { };
    extern "C" int g();
    extern "C++" int h();
}

namespace B {
    void X(int);
    extern "C" int g();
    extern "C++" int h(int);
}

using namespace A;
using namespace B;
void f() {
   X(1); // error: name X found in two namespaces
   g();  // OK: name g refers to the same entity
   h();  // OK: overload resolution selects A::h
}

——尾注 ]

评论

0赞 Barry 7/30/2015
@Supremum 声明的意思是声明。该示例说明,在 中声明的函数不会隐藏在 中声明的类。using-declarations 只是表示可以在 中找到。XBXAX::
0赞 Supremum 7/30/2015
在不同的命名空间中声明的确切含义是什么?using-declarations呢?例如,clang 使用 N::i 接受命名空间 N { static int i = 1; } namespace M { struct i {};使用 M::i;int main() { sizeof (i); } 但如果更改 using-declaration 命令,则会以歧义拒绝。我想只有最初声明它的命名空间才算数(不是通过 using 声明声明声明),所以 clang 在这两种情况下都应该拒绝。
0赞 Supremum 7/30/2015
当人们谈论声明时,我总是因为使用声明而感到困惑。
0赞 Supremum 7/30/2015
好的,所以 using-declarations 只是将名称引入它们出现的声明性区域,但它们没有声明任何东西。它们仅指通过非使用声明(可能间接通过其他使用声明)对实体进行的声明。是吗?
0赞 Supremum 7/30/2015
我真的不喜欢 c++ 标准呈现“好像”规则的事实。你应用它们并认为你已经推导出了一些东西,但随后标准的其他地方施加了一个限制,阻止了“好像”规则的特定应用。而另一个地方可能离“好像”规则的提出很远。在这种情况下,[basic.lookup.unqual] 部分不涵盖 [basic.scope.hiding]p2,而只涵盖 [basic.scope.hiding]p1 也无济于事。