简体   繁体   English

安全地将整数与强类型枚举进行比较

[英]Safely compare integer to strongly typed enum

How can one safely compare an integral value, of unknown type, to a strongly typed enum, when it's possible that the integral value may not be within the range of enumeration values? 当积分值可能不在枚举值范围内时,如何安全地将未知类型的积分值与强类型枚举进行比较?

The most obvious way to compare an integral value to an enum would be to cast the integral value, a , to the enum type, E , and compare to the enumeration value b , like this: 将积分值与枚举进行比较的最明显方法是将积分值a转换为枚举类型E ,并与枚举值b进行比较,如下所示:

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

However, if a is not in the range of enumeration values, this leads to unspecified behavior, per [expr.static.cast]/10: 但是,如果a不在枚举值的范围内,则会导致未指定的行为,对于[expr.static.cast] / 10:

A value of integral or enumeration type can be explicitly converted to an enumeration type. 可以将整数或枚举类型的值显式转换为枚举类型。 The value is unchanged if the original value is within the range of the enumeration values (7.2). 如果原始值在枚举值(7.2)的范围内,则该值不变。 Otherwise, the resulting value is unspecified (and might not be in that range). 否则, 结果值未指定 (可能不在该范围内)。

This can be seen in the resulting failure ( compare as above): 这可以在结果失败中看出( compare如上):

enum E : uint8_t { A = 0 };
compare(256, E::A); // returns true, 256 == E::A, but E::A = 0

One could instead cast the enum to the integral type, but this can lead to incorrect results if the integral type can not represent all the enum values: 可以将枚举转换为整数类型,但如果整数类型不能表示所有枚举值,则可能导致错误的结果:

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

The enum can be cast to its underlying integral type, which is guaranteed to be able to represent all of the enumeration's values. 枚举可以转换为其底层整数类型,保证能够表示所有枚举值。

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

There's still a possible issue with the usual arithmetic conversions if the integral type and the enumeration type differ in signededness. 如果整数类型和枚举类型在signededness上不同,那么通常的算术转换仍然存在问题。

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

Here, E::A = (int32_t)-1 is converted to unsigned int , which can not represent -1, converting it to (most likely) 4294967295. 这里, E::A = (int32_t)-1被转换为unsigned int ,它不能表示-1,将其转换为(最有可能)4294967295。

This conversion, of an integer to another integral type which can not represent its value, can only happen if one type is unsigned and the other has a negative value (and thus must be of signed type). 只有当一个类型是无符号的而另一个类型具有负值(因此必须是有符号类型)时,才会发生整数到另一个不能表示其值的整数类型的转换。 Since an unsigned value and a negative value cannot possibly be equal, we can tell the result of the comparison without needing to compare the exact values. 由于无符号值和负值不可能相等,我们可以告诉比较结果而无需比较确切的值。

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);
}

This will correctly catch the cases were a negative value would be converted to an unsigned value that could then match the other operand. 这将正确捕获案例,如果负值将转换为无符号值,然后可以匹配另一个操作数。 Since the compiler knows the types at compile time, it can optimize the sign check expression to nothing, a<0 , or b<0 as appropriate for the types of a and b . 由于编译器在编译时知道类型,它可以将符号检查表达式优化为空, a<0b<0适合于ab的类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM