4

在 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-directives 交互。

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

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

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

在标准的其他地方我发现 [namespace.udir]p2 ( http://eel.is/c++draft/namespace.udir#2 ) 我认为这是回答这个问题的关键:

using-directive 指定指定命名空间中的名称可以在 using-directive 出现在 using-directive 之后的范围内使用。在非限定名称查找 ([basic.lookup.unqual]) 期间,名称看起来好像它们是在 最近的封闭命名空间中声明的,其中包含使用指令和指定命名空间。[注:在此上下文中,“包含”是指“直接或间接包含”。——尾注]

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

因此,我认为我们可以通过类似地将 [namespace.udir]p2 的as if部分应用于 [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); }

根据我的解释,这个程序应该是格式正确的,i应该作为整数变量来查找。Clang 和 GCC 都不同意这一点,因为它给出了名称查找歧义。

在 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++ 标准?

4

2 回答 2

3

我认为这里的关键短语是:

可以通过在嵌套声明区域或派生类 (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); }

两个is 都在不同的非嵌套范围中声明,因此没有隐藏。名称查找会发现它们就像在中声明的一样::,但这不是隐藏规则所规定的。

否则,我们有,来自 [basic.lookup]:

名称查找应找到名称的明确声明(见 10.2)。如果名称查找发现名称是函数名称,则名称查找可能会将多个声明与名称相关联;

中没有明确的声明::,所以这段代码格式不正确,错误是正确的。另一个示例也是如此,因此存在一些使用声明顺序,clang 编译它的事实是一个错误。

虽然这是非规范性的,但 [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
}

——尾注]

于 2015-07-29T14:26:22.777 回答
2
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;

…不适用于您的情况,因为这两个is 在不同的范围内声明。还,

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

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

于 2015-07-29T14:25:45.453 回答