在枚举中转换整数的安全方法

时间:2013-07-23 13:10:51

标签: c++ c++11

如果我将整数转换为枚举类会发生什么,但枚举中不存在该值?例如:我想要一个函数来测试整数是否具有来自枚举类的某个值:

enum class EnumClass { A, B = 4, C = 9, D = 60 };

bool checkEnumClass( int v )
{
    switch( static_cast< EnumClass >( v ) )
    {
    case EnumClass::A:
    case EnumClass::B:
    case EnumClass::C:
    case EnumClass::D:
        return true;
    default:
        return false;
    }
}

checkEnumClass( 0 ) == true;
checkEnumClass( 7 ) == false;   // is this true?

这是检查整数是否可以转换为枚举的正确方法吗?

4 个答案:

答案 0 :(得分:14)

我认为没有比OP提供的解决方案更好的解决方案。但是,它有一个小缺陷,我可以建议(非标准)解决方法。

问题如下。假设今天的代码与OP中的代码相同,但有一天,有人向EnumClass添加了一个新的枚举器,它变为:

enum class EnumClass { A, B = 4, C = 9, D = 60, E = 70 };

也假设此人忘记更新checkEnumClass的定义(这不太可能发生,特别是如果代码在另一个文件中)。然后,

checkEnumClass( 70 );
尽管70现在是有效值,但

将返回false。单元测试可能有助于捕获此错误,但此人必须记住更新测试。 (回想一下,他们忘了在第一时间更新代码!)

不幸的是,标准C ++没有提供强制switch enum覆盖所有情况的方法(与提供final switch statement的D不同)

但是,有一些特定于编译器的功能可以为您完成此任务。

对于GCC(我相信,也是Clang),你可以添加编译器选项-Wswitch(或-Wall,暗示-Wswitch)。对于Visual Studio,您可以添加

#pragma warning(error : 4062)

包含checkEnumClass的文件(包含枚举定义的文件)

最后,您必须稍微更改checkEnumClass,因为default标签告诉编译器所有案例都已涵盖。代码应该是这样的:

bool checkEnumClass( int v )
{
    switch( static_cast< EnumClass >( v ) )
    {
    case EnumClass::A:
    case EnumClass::B:
    case EnumClass::C:
    case EnumClass::D:
        return true;
    }
    return false;
}

通过此解决方法,包含枚举器E但忘记更新checkEnumClass的人员将收到以下错误/警告:

GCC

  

警告:在交换机[-Wswitch]

中未处理枚举值“E”

Visual Studio:

  

错误C4062:未处理枚举'EnumClass'开关中的枚举器'E'   switch(static_cast&lt; EnumClass&gt;(v))

更新1 :遵循elvis.dukaj的评论。

作为一种好的做法,将-Werror添加到GCC的选项中,将所有警告变为错误。

更新2 -Wswitch -Wswitch-enum优于-Werror,即使有default,也会引发警告(或{{1}}时出错)标签。不幸的是,我不知道Visual Studio中有任何类似的功能。

答案 1 :(得分:7)

枚举可以保存其最小值和最大值之间的任何值,因此您拥有的主要是正确的。您还需要做的唯一事情是确保整数参数在适当的范围内,因为如果您尝试转换一个超出枚举范围的int,则您有未定义的行为:

bool checkEnumClass( int v )
{
    if (v < static_cast<int>(EnumClass::A)) return false;
    if (v > static_cast<int>(EnumClass::D)) return false;

    switch( static_cast< EnumClass >( v ) )
    {
    case EnumClass::A:
    case EnumClass::B:
    case EnumClass::C:
    case EnumClass::D:
        return true;
    default:
        return false;
    }
}

答案 2 :(得分:4)

如果您需要对枚举值进行编译时检查,可以试试这个:

template <int I> struct check_enum { static const bool value = false; };

template <> struct check_enum<static_cast<int>(EnumClass::A)>
{ static const bool value = true; };

template <> struct check_enum<static_cast<int>(EnumClass::B)>
{ static const bool value = true; };

template <> struct check_enum<static_cast<int>(EnumClass::C)>
{ static const bool value = true; };

template <> struct check_enum<static_cast<int>(EnumClass::D)>
{ static const bool value = true; };

然后,您可以这样使用它:

static_assert(check_enum<0>::value, "invalid enum value"); // ok!
static_assert(check_enum<1>::value, "invalid enum value"); // compile error

Live demo

编辑:使用C ++ 14模板变量可以采用相同的方法。

template <int I> constexpr bool check_enum = false;
template <> constexpr bool check_enum<static_cast<int>(EnumClass::A)> = true;
template <> constexpr bool check_enum<static_cast<int>(EnumClass::B)> = true;
template <> constexpr bool check_enum<static_cast<int>(EnumClass::C)> = true;
template <> constexpr bool check_enum<static_cast<int>(EnumClass::D)> = true;

static_assert(check_enum<0>, "invalid enum value"); // ok!
static_assert(check_enum<1>, "invalid enum value"); // compile error

这些方法的主要缺点是专注于每一个价值的努力,你必须考虑努力是否值得。如果遗漏了某些价值,那么很难找到并解决问题。

答案 3 :(得分:-2)

只需检查int是否大于check类中的最大可能值,不需要switch语句,只需使用if语句,或者更好,只需要一个bool。

bool checkEnumClass(int i)
{
    return (i <= 7);
}