安全地比较整数和强类型枚举类型

6

如果一个整数值类型未知,如何安全地将其与强类型枚举进行比较,当这个整数值可能不在枚举值的范围内时?

将整数值 a 强制转换为枚举类型 E,并与枚举值 b 进行比较是最明显的比较方法,示例代码如下:

template <typename I, typename E>
bool compare(I a, E b) { return static_cast<E>(a) == b; }

然而,如果a不在枚举值的范围内,这将导致未指定的行为,根据[expr.static.cast]/10:
整数或枚举类型的值可以被显式转换为枚举类型。如果原始值在枚举值的范围内(7.2),则该值不变。否则,结果值是未指定的(可能不在该范围内)。
这可以在上述失败中看到(如上所述的compare)。
enum E : uint8_t { A = 0 };
compare(256, E::A); // returns true, 256 == E::A, but E::A = 0

您可以将枚举类型转换为整型,但如果整型无法表示所有枚举值,则可能导致不正确的结果:

enum E : int { A = 256 };
template <typename I, typename E>
bool compare(I a, E b) { return a == static_cast<I>(b); }
compare((uint8_t)0); // returns true, 0 == E::A, but E:A = 256
1个回答

11

枚举类型可以转换为其底层整数类型,保证能够表示该枚举类型的所有值。

template <typename I, typename E>
bool compare(I a, E b) { return a == static_cast<std::underlying_type_t<E>>(b); }

如果整数类型和枚举类型的有符号性不同,则仍可能存在 通常算术转换 的问题。

enum class : int32_t { A = -1 };
compare(4294967295u, E3::A); // returns true

在这里,将E::A=(int32_t)-1转换为unsigned int,但是unsigned int无法表示-1,因此会将其转换为(最有可能的)4294967295。

只有当一个类型是无符号的而另一个类型具有负值(必须是带符号的类型),才能发生整数到另一种不能表示它的整数类型的转换。由于无符号值和负值不可能相等,我们可以在不需要比较确切值的情况下确定比较结果。

template <typename I, typename E>
bool compare(I a, E b) {
    using UTE = std::underlying_type_t<E>; 
    return !(std::is_unsigned_v<I> && static_cast<UTE>(b) < 0) &&
           !(std::is_unsigned_v<UTE> && a < 0) &&
           a == static_cast<UTE>(b);
}

这将正确地捕获将负值转换为无符号值的情况,该无符号值可以与另一个操作数匹配。由于编译器在编译时知道类型,因此它可以根据ab的类型将符号检查表达式优化为nothinga<0b<0


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接