我看到reinterpret_cast
被用于将增量应用于枚举类,我想知道这种用法在标准C++中是否可以接受。
enum class Foo : int8_t
{
Bar1,
Bar2,
Bar3,
Bar4,
First = Bar1,
Last = Bar4
};
for (Foo foo = Foo::First; foo <= Foo::Last; ++reinterpret_cast<int8_t &>(foo))
{
...
}
我知道在普通类的情况下,强制转换到基类的引用是安全的。但是由于枚举类不是事件隐式转换到它们的底层类型,我不确定上面的代码是否以及如何保证在所有编译器中工作。有线索吗?
3条答案
按热度按时间zfciruhq1#
如果你真的想迭代枚举的值,你可能需要为你的枚举重载运算符
++
:并使用
要回答是否允许
reinterpret_cast
的问题,请从5.2.10/1开始:5.2.10重新解释强制转换[表达式重新解释强制转换]
1表达式
reinterpret_cast<T>(v)
的结果是将表达式v
转换为类型T
的结果,如果T
是对函数类型的左值引用类型或右值引用,则结果为左值;如果T
是对对象类型的右值引用,则结果是x值;否则,结果为右值,并对表达式v
执行左值到右值(4.1)、数组到指针(4.2)和函数到指针(4.3)的标准转换。下面列出了可以使用reinterpret_cast
显式执行的转换。不能使用reinterpret_cast
显式执行其他转换。(着重号是我的)
根据5.2.10/11,使用参考文献的重新解释基于指针:
11如果类型为"指向
T1
的指针"的表达式可以使用reinterpret_cast
显式转换为类型"指向T2
的指针",则类型为T1
的glvalue表达式可以转换为类型"指向T2
的引用"。结果引用与源glvalue相同的对象,但具有指定的类型。[* 注意:* 也就是说,对于左值,引用转换reinterpret_cast<T&>(x)
与使用内置&
和*
运算符的转换*reinterpret_cast<T*>(&x)
具有相同的效果(对于reinterpret_cast<T&&>(x)
也是类似的)。-* 结束注意 *]不创建临时对象,不进行复制,也不调用构造函数(12.1)或转换函数(12.3)。这就把问题从这个转变为:
这是否合法
下一站是5.2.10/7:
7对象指针可以显式地转换为不同类型的对象指针。当类型"指向
T1
的指针"的纯右值v
转换为类型"指向 * cv *T2
的指针"时,如果T1
和T2
都是标准布局类型,则结果为static_cast<
*cv
*T2*>(static_cast<
*cv
*void*>(v))
(3.9),且T2
的对齐要求不严格于T1
。或者如果任一类型是void
,则将类型为"指向T1
的指针"的右值转换为类型"指向T2
的指针"(其中T1
和T2
是对象类型,并且T2
的对齐要求不比T1
的对齐要求更严格),然后返回到其原始类型,将生成原始指针值。任何其他此类指针转换的结果都是未指定的。给定Python 3.9/9,
int8_t
和枚举类型都是标准布局类型,现在问题转换为:static_cast
是在5.2.9中定义的,没有任何东西可以使上述行为合法--事实上,5.2.9/5明确暗示了它是非法的。其他条款也无济于事:T*
-〉void*
-〉T*
,其中T
必须相同(省略 * cv *)我的结论是你的代码
是不合法的,其行为不是由标准定义的。
还要注意的是,上面提到的5.2.9/9和5.2.9/10负责使代码合法,我在最初的答案中给出了代码,您仍然可以在顶部找到代码。
jdgnovmf2#
增量通过不同类型的左值访问
foo
的值,这是未定义的行为,除了3.10 [basic.lval]中列出的情况。枚举类型及其底层类型不在该列表中,因此代码具有未定义的行为。对于一些支持非标准扩展的编译器,你可以通过类型双关来实现:
但是这也不是可移植的,因为在将值存储在
intenum::e
中之后访问intenum::i
是不允许的。但是为什么不直接使用整数并根据需要进行转换呢?
这是便携式和安全的。
(It IMHO仍然不是一个好主意,因为可能有几个枚举器具有相同的值,或者枚举类型的值没有对应的枚举器标签。)
xv8emn3q3#
只要强制转换为枚举的确切基础类型,它就是安全的。
如果枚举类的基础类型发生更改,则
++reinterpret_cast<int8_t &>(foo)
将静默中断。更安全的版本:
或者,