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

elv*_*kaj 13 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?
Run Code Online (Sandbox Code Playgroud)

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

Cas*_*eri 14

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

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

enum class EnumClass { A, B = 4, C = 9, D = 60, E = 70 };
Run Code Online (Sandbox Code Playgroud)

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

checkEnumClass( 70 );
Run Code Online (Sandbox Code Playgroud)

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

不幸的是,标准C++不提供一种方法来强制switchenum,以覆盖所有的情况下(与d它提供的final switch 声明).

但是,有一些特定于编译器的功能可以为您执行此操作.

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

#pragma warning(error : 4062)
Run Code Online (Sandbox Code Playgroud)

到包含的文件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;
}
Run Code Online (Sandbox Code Playgroud)

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

GCC:

警告:在交换机[-Wswitch]中未处理枚举值"E"

视觉工作室:

错误C4062:枚举'EnumClass'的开关枚举'E'不被处理
交换机(的static_cast <EnumClass>(v))中

更新1:继elvis.dukaj的评论之后.

作为一个好的做法,添加-WerrorGCC的选项可以将所有警告变成错误.

更新2:知足者常乐-Wswitch-Wswitch-enum这将提高警告(或错误如果-Werror),即使有一个default标签.不幸的是,我不知道Visual Studio中有任何类似的功能.


Vau*_*ato 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 强制转换枚举值范围之外的int是不合法的,因此必须先进行检查. (2认同)

Pap*_*ter 5

如果您需要对枚举值进行编译时检查,您可以尝试以下操作:

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; };
Run Code Online (Sandbox Code Playgroud)

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

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

现场演示

编辑:对于 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
Run Code Online (Sandbox Code Playgroud)

这些方法的主要缺点是专门化每个值的努力,您必须考虑这种努力是否值得。如果遗漏了某些价值,则可能很难找到并解决问题。