2017-12-03 224 views
3

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

最明显的方法来整数值进行比较,以枚举将投积分值,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:

整型或枚举类型的值可以显式转换到一个enumerati类型。如果原始值在枚举值(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 

回答

8

枚举可以转换为其基础整型,这可以保证能够表示所有枚举的值。

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

仍然有可能发行与通常的算术转换如果积分类型和枚举类型signededness不同。

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

这里,E::A = (int32_t)-1被转换成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类型的适当值,即a<0或。