C ++标准在[basic.scope.hiding]中使用“相同范围”究竟意味着什么?

时间:2019-10-03 13:32:16

标签: c++ scope language-lawyer name-lookup

在C ++ 14标准中,[basic.scope.hiding]第2段(3.3.10.2)表示:

  

可以通过变量,数据成员,   函数或在相同作用域中声明的枚举数。如果在相同作用域(以任意顺序)中声明了具有相同名称的类或枚举名称以及变量,数据成员,函数或枚举器,则无论变量位于何处,该类或枚举名称都将被隐藏。 ,数据成员,函数或枚举器名称可见。

我对“相同范围”一词感到不安。的确,在下面的代码片段中,在同一范围内声明类C和变量C的情况是什么?

namespace case_1 {
    int C;
    class C;
    // Here C refers to variable
}

namespace case_2 {
    class C;

    namespace nested {
        int C;
        // Here C refers to variable
    }
}

namespace case_3 {
    int C;

    namespace nested {
        class C;
        // Here C refers to class
    }
}

namespace case_4 {
    enum Enum { A, B, C };
    class C;
    // Here C refers to enumerator
}

假设1:“相同范围”表示“相同块”

如果我们坚持这一假设,则案例1应受规则3.3.10.2的关注。情况2呢?我想它已包含在规则3.3.10.1中:

  

可以通过在嵌套的声明性区域或派生类中使用相同名称的显式声明来隐藏名称。

此外,该假设很好地说明了情况3,其中类名隐藏了变量名(而不是逆名称),但不能解释情况4。实际上,C枚举数在与C类,但该类仍被隐藏。

假设2:“在相同范围内声明”表示“具有完全相同的范围”

如果这个假设是正确的,那么规则中就不会涉及我的代码中描述的情况,因为两个名称不可能具有完全相同的作用域。即使case_1中的变量也具有与类不同的作用域。实际上,变量名的范围始于其声明之后,因此早于类名。

假设3:“在同一范围内声明”是指“其中一个名称在另一个范围内声明”

如果该假设成立,则规则3.3.10.2应涵盖上述所有情况。实际上,在case_1case_3中,C类是在C变量的范围内声明的;在case_2中,C变量在C类的范围内声明;并且在case_4中,C类在C枚举器的范围内声明。但是,case_3不遵循规则,因为它是变量“赢”并保持可见。


如您所见,我的每个假设都有一个缺点,我真的不理解该标准在该段中“相同范围”的确切含义。

2 个答案:

答案 0 :(得分:3)

  

在相同范围内声明类C和变量C的情况是什么?

情况1和4是相同的作用域。情况2和3的作用范围不同。

在这种情况下,我确实无法在标准中找到“相同”的准确定义,但是有意义且与您的测试结果相匹配的解释是比较最小的封闭范围(而不是每个声明的声明性区域 1 )。

  

假设1:“相同范围”表示“相同范围”

虽然块具有作用域,但它们不是等效的。例如,还有命名空间范围和类范围。

  

但无法解释这种情况4.确实,C枚举器在与C类不同的块中声明,但该类仍被隐藏。

枚举声明没有作用域。枚举数和类在同一范围内。

  

假设2:“在相同范围内声明”表示“具有完全相同的范围”

正如您所说,任何声明都不能具有与另一个声明完全相同的范围,因此这种解释将使该规则变得毫无意义。

  

假设3:“在同一范围内声明”是指“其中一个名称在另一个范围内声明”

这是不正确的解释,因为嵌套范围可以在一个声明的范围内,但是就所讨论的规则而言,它不是同一范围。


1 在最新的标准草案中,措词已更改为使用“声明性区域”一词,这显然与“范围”具有不同的含义。但这并不会改变规则的预期含义。

答案 1 :(得分:2)

在这里,从范围上讲,它们是可见性。
当且仅当在某个地方同时知道两个名称时,这两个名称在同一范围内(如果它们相同,则名称将变得模棱两可/冲突)。
“已知”是指您可以直接直接访问它们(如果名称相同,则使用完全相同的语法)。


  

实际上, C枚举器在与C类不同的块中声明,但该类仍处于隐藏状态。

这是错误的。
实际上,enum并不将其成员封装在本地范围内。换句话说,enum成员与enum本身处于同一范围。

例如,如果我声明

enum MY_ENUM {A, B};

我可以通过My_ENUM::AMy_ENUM::B或仅通过AB访问值。此外,允许隐式转换为int

如果要将值封装在MY_ENUM本地的作用域中(并因此禁止AB进行访问),则必须声明{{1 }}改为enum

enum class

但是,将不再允许隐式转换为enum class MY_ENUM {A, B};