【问题标题】:Interpretation of [basic.scope.hiding]p2 when unqualified name lookup involves using-directives非限定名称查找涉及 using-directives 时 [basic.scope.hiding]p2 的解释
【发布时间】:2015-10-20 13:58:03
【问题描述】:

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])可以是 被变量、数据成员、函数或枚举器的名称隐藏 在同一范围内声明。如果一个类或枚举名称和一个 变量、数据成员、函数或枚举器在 具有相同名称、类或枚举的相同范围(以任何顺序) name 隐藏在变量、数据成员、函数或 枚举器名称可见。

我很想知道在执行非限定名称查找时名称隐藏如何与 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 指令出现的范围内使用 使用指令。在不合格名称查找期间 ([basic.lookup.unqual]),名称出现就好像它们是在中声明的 最近的封闭命名空间,其中包含 using-directive 和指定的命名空间。 [注:在这种情况下, “包含”是指“直接或间接包含”。 ——尾注]

将此规则的 as if 部分应用于 [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++ 标准?

【问题讨论】:

    标签: c++ language-lawyer using-directives name-lookup


    【解决方案1】:

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

    一个名字可以被一个同名的显式声明隐藏在一个嵌套的声明区域或派生 类 (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)。名称查找可能关联多个声明 如果它发现名称是函数名称,则带有名称;

    :: 中没有明确的声明,所以这段代码格式不正确,错误是正确的。另一个示例也是如此,因此存在一些 using-declaration 顺序供 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
    }
    

    ——尾注]

    【讨论】:

    • @Supremum Declared 表示已声明。该示例说明B 中声明的函数X 不会隐藏A 中声明的类X。 using-declarations 只是意味着 X:: 中找到。
    • 在不同的命名空间中声明究竟是什么意思?使用声明呢?例如 clang 接受命名空间 N { static int i = 1; } 命名空间 M { 结构 i {}; } 使用 N::i;使用 M::i; int main() { sizeof (i); } 但如果更改 using-declaration 顺序,则会含糊地拒绝。我猜只有最初声明的命名空间才算(不是通过 using-declarations 声明),所以在这两种情况下都应该拒绝。
    • 当人们谈论声明时,我总是因为使用声明而感到困惑。
    • 好的,所以 using-declarations 只是将名称引入它们出现的声明区域,但它们没有声明任何东西。它们只是指通过非使用声明(可能间接通过其他使用声明)对实体的声明。对吗?
    • 我真的不喜欢 c++ 标准呈现“好像”规则的事实。您应用它们并认为您已经推断出某些东西,但是标准的其他地方施加了限制,阻止了“好像”规则的特定应用。而那个其他地方可能与“好像”规则的呈现相去甚远。在这种情况下,[basic.lookup.unqual] 部分不涵盖 [basic.scope.hiding]p2,而仅涵盖 [basic.scope.hiding]p1 也无济于事。
    【解决方案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]) 期间,名称 看起来好像它们是在最近的封闭命名空间中声明的 其中包含 using 指令和指定的命名空间。

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

    【讨论】:

      猜你喜欢
      • 2015-10-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-11
      • 2016-02-08
      • 1970-01-01
      • 2015-10-16
      • 2014-08-05
      相关资源
      最近更新 更多