为什么从char强制转换为std :: byte可能是未定义的行为?

时间:2018-09-28 11:09:36

标签: c++ language-lawyer

C ++ 17的std::byte必须是枚举类:

enum class byte : unsigned char {};

我们可能希望使用std::byte来表示原始内存,而不是char之一,因为它具有更好的类型安全性,已定义其特定于字节的运算符,并且不能提升为{ {1}}像int一样出乎意料。我们需要使用显式强制转换或charto_integer转换为其他整数。但是,从许多来源我们仍然可以获得std::byte(或更可能是char的整个缓冲区),因此可能要转换它:

char

void fn(char c) { std::byte b = static_cast<std::byte>(c); // ... that may invoke undefined behavior, read below } 的签名是实现定义的,因此char可以是std::numeric_limits<char>::is_signed。因此,上方true的负值可能超出c的范围。

现在在8.2.9静态强制转换[expr.static.cast]第10段的C ++ 17标准中 我们可以看到:

  

整数或枚举类型的值可以显式转换为   完整的枚举类型。如果原始   值在枚举值(10.2)的范围内。除此以外,   行为是不确定的。

从10.2开始,我们可以看到上述范围是基础类型的范围。因此,为了避免未定义的行为,我们必须编写更多的代码。例如,我们可以在unsigned char上添加强制类型转换,以在强制类型转换期间实现模块化算术的定义效果:

unsigned char

我误会了吗?这不是过于复杂和限制吗?为什么具有无符号基础类型的void fn(char c) { std::byte b = static_cast<std::byte>(static_cast<unsigned char>(c)); // ... now we have done it in portable manner? } 不能像其基础类型那样遵循模块化算法?请注意,无论如何,整个转换行很可能都被编译器编译为空。 enum class自C ++ 14起必须为二进制补码,因此其按位表示形式必须与将模块算术转换为char之后的形式相同。谁从这种形式未定义的行为中受益,又如何受益?

1 个答案:

答案 0 :(得分:10)

这将在next standard中修复:

  

整数或枚举类型的值可以显式转换为完整的枚举类型。如果枚举类型具有固定的基础类型,则如果有必要,首先通过整数转换将该值转换为该类型,然后将该值转换为枚举类型。如果枚举类型没有固定的基础类型,则如果原始值在枚举值([dcl.enum])的范围内,则该值保持不变,否则行为未定义

Here's从(C ++ 11)未指定更改为(C ++ 17)未定义的原因: 

  

尽管问题1094阐明了枚举类型的表达式的值可能不在转换为枚举类型后的枚举值的范围内(请参阅8.2.9 [expr.static.cast]第10段) ,结果只是一个未指定的值。鉴于未定义的行为会使表达式变得非恒定,这一点可能应该得到加强,以产生未定义的行为。

here's和C ++ 2a修复背后的原理:

  

std :: byte(21.2.5 [support.types.byteops])和bitmask(20.4.2.1.4 [bitmask.types])的规范揭示了整数转换规则的问题,根据这两个规则在一般情况下,这些规范具有未定义的行为。问题在于,除非要转换的值在枚举范围内,否则转换为枚举类型的行为将不确定。

     

对于具有无符号固定基础类型的枚举,此要求过于严格,因为将大值转换为无符号整数类型是明确定义的