提问人:Supremum 提问时间:7/29/2015 更新时间:7/29/2015 访问量:125
当非限定名称查找涉及 using 指令时对 [basic.scope.hiding]p2 的解释
Interpretation of [basic.scope.hiding]p2 when unqualified name lookup involves using-directives
问:
在 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++ 标准?
答:
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
我认为这里的关键词是:
名称可以通过嵌套声明性区域中相同名称的显式声明来隐藏,也可以派生 类 (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 }
——尾注 ]
评论
X
B
X
A
X
::
评论