Joh*_*nck 24 c++ enums c++11 enum-class strongly-typed-enum
在C++ 11中,我们可以将强类型的枚举(enum class)强制转换为其基础类型.但似乎我们不能指向相同的指针:
enum class MyEnum : int {};
int main()
{
MyEnum me;
int iv = static_cast<int>(me); // works
int* ip = static_cast<int*>(&me); // "invalid static_cast"
}
Run Code Online (Sandbox Code Playgroud)
我试图理解为什么会这样:有什么关于枚举机制的东西让支持这个很难或没有意义吗?这是标准中的简单疏忽吗?别的什么?
在我看来,如果枚举类型真正构建在如上所述的整数类型之上,我们应该不仅能够投射值而且还能投射指针.我们仍然可以使用reinterpret_cast<int*>或者使用C风格的演员表,但这比我认为的更重要.
Fal*_*ias 13
TL; DR: C++的设计者不喜欢类型惩罚.
其他人指出为什么标准不允许这样做; 我将尝试解决为什么标准的编写者可能会这样做的原因.根据这个提议,强类型枚举的主要动机是类型安全.不幸的是,类型安全对许多人来说意味着很多东西 假设一致性是标准委员会的另一个目标是公平的,所以让我们在C++的其他相关背景下检查类型安全性.
在C++中,除非明确指定相关(通过继承),否则类型是不相关的.考虑这个例子:
class A
{
double x;
int y;
};
class B
{
double x;
int y;
};
void foo(A* a)
{
B* b = static_cast<B*>(a); //error
}
Run Code Online (Sandbox Code Playgroud)
即使A和B具有完全相同的表示(标准甚至称它们为"标准布局类型"),但如果没有a,则无法在它们之间进行转换reinterpret_cast.同样,这也是一个错误:
class C
{
public:
int x;
};
void foo(C* c)
{
int* intPtr = static_cast<int*>(c); //error
}
Run Code Online (Sandbox Code Playgroud)
即使我们知道C中唯一的东西是int,你可以自由访问它,但static_cast失败了.为什么?没有明确指出这些类型是相关的.C++旨在支持面向对象的编程,它提供了组合和继承之间的区别.您可以在通过继承关联的类型之间进行转换,但不能通过组合进行转换.
基于您所看到的行为,很明显强类型枚举通过组合与其基础类型相关联.为什么这可能是标准委员会选择的模型?
有很多关于这个问题的文章写得比我能适合的任何文章更好,但我会尝试总结一下.何时使用组合与何时使用继承肯定是一个灰色区域,但在这种情况下有许多点支持组合.
在这种情况下,您可能会争论遗传或构成是否更好,但最终必须做出决定并且行为是以构图为模型的.
相反,以稍微不同的方式看待它.你不能static_cast一long*来int*,即使int与long具有相同的底层表示.出于同样的原因,基于的枚举仍被int视为一种独特的,不相关的类型,int因此需要reinterpret_cast.
枚举是具有命名常量的不同类型(3.9.2).[...]每个枚举定义一个与所有其他类型不同的类型.[...]如果两个枚举类型具有相同的基础类型,则它们是布局兼容的.
[dcl.enum](§7.2)
底层类型指定内存中枚举的布局,而不是它与类型系统中其他类型的关系(正如标准所说,它是一个独特的类型,它自己的类型).指向a的指针enum : int {}永远不会隐式转换为a int*,就像指向a的指针一样struct { int i; };,即使它们在内存中看起来都相同.
那么为什么隐式转换首先int起作用呢?
对于其基础类型是固定的枚举,枚举的值是基础类型的值.[...]通过整数提升(4.5)将枚举数或无范围枚举类型的对象的值转换为整数.
[dcl.enum](§7.2)
因此我们可以将枚举值赋给a,int因为它们属于类型int.int由于整数提升的规则,可以将枚举类型的对象分配给a .顺便说一下,这里的标准特别指出这只适用于C风格(无范围)枚举.这意味着你仍然需要static_cast<int>在你的例子的第一行中,但是一旦你enum class : int变成一个enum : int它就会在没有显式强制转换的情况下工作.尽管指针类型仍然没有运气.
整体促销在[conv.prom](§4.5)的标准中定义.我将为您提供引用完整部分的详细信息,但这里的重要细节是,其中的所有规则都适用于非指针类型的prvalues,因此这些都不适用于我们的小问题.
最后一部分可以在[expr.static.cast](§5.2.9)中找到,它描述了如何static_cast工作.
可以将范围枚举类型(7.2)的值显式转换为整数类型.
这也解释了为什么从你投enum class来int的作品.
但请注意,static_cast指针类型允许的所有s(同样,我不会引用相当冗长的部分)需要类型之间的某种关系.如果你还记得答案的开头,那么每个枚举都是一个不同的类型,因此与它们的基础类型或同一基础类型的其他枚举没有任何关系.
这与@MarkB的答案相关:静态转换指向指针enum的指针int类似于将指针从一个整数类型转换为另一个整数类型 - 即使两者在下面都具有相同的内存布局,并且一个的值将隐式转换为另一个规则积分促销,它们仍然是无关的类型,所以static_cast在这里不起作用.
我认为思考的错误就是这样
enum class MyEnum : int {};
Run Code Online (Sandbox Code Playgroud)
不是真正的继承.当然你可以说MyEnum 是一个 int.然而,从传统的继承不同,因为没有可用的所有操作ints为可MyEnum也.
让我们将其与以下内容进行比较:圆是椭圆.但是,实现a CirlceShape继承几乎总是错误的,EllipseShape因为并非椭圆上可能的所有操作都可以用于循环.一个简单的例子是在x方向上缩放形状.
因此,将枚举类视为从整数类型继承会导致您的情况混乱.您不能递增枚举类的实例,但可以增加整数.由于它不是真正的继承,因此禁止静态地将指针转换为这些类型是有意义的.以下行不安全:
++*reinterpret_cast<int*>(&me);
Run Code Online (Sandbox Code Playgroud)
这可能是委员会static_cast在这种情况下禁止的原因.一般reinterpret_cast被认为是邪恶的,而static_cast被认为是好的.
| 归档时间: |
|
| 查看次数: |
7449 次 |
| 最近记录: |