如何安全地将整数类型转换为作用域枚举

时间:2016-06-10 15:31:26

标签: c++ c++11

C++11范围内的枚举很棒,你应该尽可能使用它们。 但是,有时您需要将整数转换为作用域枚举值(例如,如果您是从用户输入获取它)。

有没有一种安全的方法来执行此操作并检测值何时无效(即,超出枚举的允许值)?

我相信如果整数无效,那么只使用static_cast会导致未定义的行为。是否有一种通用的方法,不需要手动为每个作用域枚举类型编写转换函数(每次向枚举添加新值时都需要更新)?

3 个答案:

答案 0 :(得分:3)

常见的方法是在枚举中加入结束标记

enum class Colors : char
{
  Red,
  Green,
  Blue,
  Last_Element
}

使用此方法,转换时,您可以检查您使用的值是否小于Last_Element的值。

考虑以下功能:

template<typename T>
typename std::enable_if<std::is_enum<T>::value, bool>::type
  IsValidEnumIntegral<T>(int integral)
{
  return (int)(T::Last_Element) > integral;
}

你可以像这样使用它:

if(IsValidEnumIntegral<Colors>(2))
  //do whatever

这适用于您使用名为Last_Element的元素创建的任何枚举。您可以进一步创建类似的功能,然后为您自动转换。

注意:未经测试。我目前无法这样做,但我认为这可行。

编辑:这只有在相关枚举使用一组整数且其元素没有间隙的情况下才有效。提供的函数也会假设枚举不包含负整数,但可以很容易地将First_Element添加到其中。

答案 1 :(得分:3)

[dcl.enum] / 8:

  

对于其基础类型是固定的枚举,其值为   枚举是基础类型的值。

这包括所有作用域枚举,因为作用域枚举的基础类型默认为int

  

可以使用 enum-base 显式指定基础类型。   对于作用域枚举类型,如果不是,则基础类型为int   明确指定。在这两种情况下,基础类型都是   据说已修复

因此,通过验证输入值是否在枚举的基础类型范围内(您可以使用std::numeric_limitsstd::underlying_type进行检查),您可以确定{{1总是会有明确定义的行为。

但是,如果程序的其余部分没有准备好处理枚举基础类型范围内的每个值,那还不够。在这种情况下,你必须自己进行验证,可能还有@Altainia的回答。

答案 2 :(得分:1)

我在一些代码库中看到过这种事情的另一种方式,基本上是使用类型特征来用额外的信息来装饰你的范围内的枚举。

这个想法是你会有一些像

这样的特质
namespace mpl {
    template <typename T>
    struct GetEnumData;
}

然后,当您声明“智能”枚举时,您还将专门化此特征链接到包含有关范围枚举的元数据的结构,例如合法值。或者,与枚举值名称对应的字符串列表,因此您可以进行枚举到字符串转换并返回。

enum class my_enum { a, b, c };
namespace mpl {
  template<>
  struct GetEnumData<my_enum> {
    static constexpr std::size_t my_number = 3;
    static const char * const my_strings [] () {
      return {"a", "b", "c"};
    }
    static const int my_values [] () {
      return {0, 1, 2};
    }
  }
} // end namespace mpl

N.B。生成上面的类型特征有点单调乏味,所以你最终会使用一些宏来表示你的“智能枚举”声明。如果你真的不喜欢宏,那么这种方法不适合你。至少直到C ++在未来的版本中添加了一些内省特征(手指交叉)。

虽然你有这种类型的特征,你可以做一些有用的事情,比如“enum_cast”,它会将一个字符串解析为枚举。

template <typename T>
T enum_cast(const std::string & input) {
  using data = GetEnumData<T>;

  for (std::size_t idx = 0; idx < data::my_number; ++idx) {
    if (data::my_strings()[idx] == input) {
      return static_cast<T>(data::my_values()[idx]);
    }
  }
  throw bad_enum_value(input);
}

你可以为int或其他整数类型做类似的事情。