嵌套枚举和嵌套类具有不同的行为

时间:2014-05-17 13:42:09

标签: c++

考虑以下两个例子:

struct X
{
    class E { static const int z = 16 };
    static const int b = X::z;   // X has no member z
};

struct Y 
{
    enum E { z = 16 };
    static const int b = Y::z;   // OK
};

是否有标准的一部分解释了这种行为?

3 个答案:

答案 0 :(得分:5)

是的,C ++标准中有这样的部分。

第一个是

9.9嵌套类型名称

  

1类型名称遵循与其他名称完全相同的范围规则。在   特别是,类定义中定义的类型名称不能   在没有资格的情况下在课外使用

更准确地说引用以下引用

  

2类成员的名称只能按如下方式使用: - 在   同类的范围(如上所述)或派生的类别(第10条)   从它的班级, - 之后。运算符应用于表达式   它的类的类型(5.2.5)或从它的类派生的类, - 之后    - >运算符应用于指向其类对象的指针(5.2.5)   或者从类中派生的类, - 在:: scope解析之后   operator(5.1)应用于其类的名称或派生的类   从班上来。

,第二个是

11.7嵌套类

  

1嵌套类是成员,因此具有相同的访问权限   任何其他成员。附上课程的成员没有特别之处   访问嵌套类的成员; 通常的访问规则(第11条)   应遵守

在此定义中,如果不考虑拼写错误(z定义后没有分号)

struct X
{
    class E { static const int z = 16 };
    static const int b = X::z;   // X has no member z
};

您正在尝试访问z 1)而没有资格,2)具有私有访问控制。

正确的定义可能看起来像

struct X
{
    class E { public: static const int z = 16; };
    static const int b = E::z;
};

对于枚举,未被覆盖的枚举的枚举器是定义枚举的类的成员。

9.2班级成员

  

1类定义中的成员规范声明了完整集   班级成员;其他任何成员都无法添加。的成员   类是数据成员,成员函数(9.3),嵌套类型和   的枚举

答案 1 :(得分:3)

  

[C++11: 7.2/10]:每个 enum-name 和每个 unscoped 枚举器都在包含 enum-specifier 的范围内声明。 [..]

类没有这样的规则。

答案 2 :(得分:0)

您要求引用标准,并且您已找到正确的引文,但似乎它们没有帮助:

  

这是如何暗示可以从外部声明区域访问枚举成员? - St.Antario

     

+1到St.Antario。我不明白。 - zavhoz

所以我会尝试调查方法。如果我们修复你的无关错误 示例struct X

  • 16岁后遗失;
  • E::z是私有的。

我们有:

struct X
{
    struct E { static const int z = 16; };
    static const int b = X::z;
};

编译你的代码如此修复,clang 3.4抱怨:

error: no member named 'z' in 'X'; did you mean 'E::z'?
static const int b = X::z;
                     ^~~~
                     E::z

(gcc 4.9.0和VC ++ 2013在同一行提供错误,但帮助较少 诊断)

然而,没有关于Y::z的此类抱怨。教训似乎是:

  • 类定义声明封闭中的类成员 范围。

  • 普通的枚举定义 声明了枚举器 封闭范围。

我在这里说普通的枚举,因为从C ++ 11开始,我们也有了一位新的爱好者 有点枚举,但Y::E是一个普通的老词。

如果这是教训,它似乎没有任何特别之处 与嵌套有关。

来自莫斯科的Vlad已经从 11.7嵌套课程中引用了标准段落 他可能希望你收集那些筑巢与你的无关 难题。但是编译器barf在限定名称X::z的事实 并且没有限定名称Y::z的barf使它看起来像 在结构E中嵌套 struct X具有一定的嵌套性 结构E中的枚举 Y没有。

回想一下,对于在范围N中声明的任何名称SS::N范围内的S 冗余,就像::N一样 对于全局名称N,资格z在全局范围内是多余的。

如果X [Y]的范围内声明了X,则属于 Y [z]您可以将其称为struct X { struct E { static const int z = 16; }; static const int b = z; }; struct Y { enum E { z = 16 }; static const int b = z; }; 。所以,让我们删除多余的东西 示例代码中的资格:

error: use of undeclared identifier 'z'; did you mean 'E::z'?
static const int b = z;
                     ^
                     E::z

现在看看铿锵有什么:

X

Y中存在同样的问题;在X中仍然没有问题。但是由于 我们已经摆脱了多余的资格,Y 根本没有被提及。

X 从未被提及。所以也许现在它看起来不再那样了 嵌套与谜题有关。

接下来让我们继续前进,摆脱Ystruct E { static const int z = 16; }; static const int b = z; enum E1 { z = 16 }; static const int b = z; int main() { return 0; } 。这是程序:

error: use of undeclared identifier 'z'; did you mean 'E::z'?
static const int b = z;
                     ^
                     E::z

并且clang说:

struct E { static const int z = 16; };
static const int b = z;

完全没有变化。

因此,嵌套确实与谜题毫无关系。事情的事实是 struct | class成员在struct | class作用域中声明,不是 在封闭范围内,普通旧枚举的枚举器在声明中声明 封闭范围。 Orbit中的Lightness Races为您提供了标准参考 对于后一事实。

这仍然让你感到惊讶吗?很难让你感到惊讶:

z

未在与b相同的范围内声明enum E1 { z = 16 }; static const int b = z; 。所以可能让你大吃一惊:

z

在与b相同的范围内声明int?好吧,那就是简单的老枚举 一直以来,因为C ++继承了它们和C.这主要是因为它 他们普通的枚举。

在C ++ 11中,普通旧枚举的范围提升特性是其原因 行话,普通的旧枚举称为 unscoped enums 。而这个特点就像 以及未见范围的调查员对enum struct SE : short { z = 16 }; //const int b = z; <- Undeclared identifier //const short b = SE::z; <- No implicit conversion SE b = SE::z; //OK const int c = static_cast<int>(SE::z); //OK 衰变的准备情况 使用无范围的枚举长期成为C ++中不舒服的根源。

因此,在C ++ 11中,我们现在有一个更严格的替代方案,称为范围的枚举枚举类,它是这样的:

{{1}}

看起来scoped enums对于范围界定不会让你感到惊讶 他们的普查员,但通过抵制隐含而使你感到惊讶 积分转换。

Further reading