C++11
范围内的枚举很棒,你应该尽可能使用它们。
但是,有时您需要将整数转换为作用域枚举值(例如,如果您是从用户输入获取它)。
有没有一种安全的方法来执行此操作并检测值何时无效(即,超出枚举的允许值)?
我相信如果整数无效,那么只使用static_cast会导致未定义的行为。是否有一种通用的方法,不需要手动为每个作用域枚举类型编写转换函数(每次向枚举添加新值时都需要更新)?
答案 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_limits
和std::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
或其他整数类型做类似的事情。