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

C++ 转换枚举中整数的安全方法,c++,c++11,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:

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

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?
enum类enum类{A,B=4,C=9,D=60};
布尔检查枚举类(int v)
{
开关(静态_转换(v))
{
案例枚举类::A:
案例枚举类::B:
案例枚举类::C:
案例枚举类::D:
返回true;
违约:
返回false;
}
}
checkEnumClass(0)=真;
checkEnumClass(7)=false;//这是真的吗?

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

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

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

return(i枚举可以保存其最小值和最大值之间的任何值,因此您所拥有的基本上是正确的。另外,您需要做的唯一一件事是确保integer参数在正确的范围内,因为如果您尝试强制转换枚举范围外的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;
    }
}
bool checkEnumClass(int v)
{
if(vstatic_cast(EnumClass::D))返回false;
开关(静态_转换(v))
{
案例枚举类::A:
案例枚举类::B:
案例枚举类::C:
案例枚举类::D:
返回true;
违约:
返回false;
}
}

我看不到任何比OP提供的解决方案从根本上更好的解决方案。但是,它有一个小缺陷,我可以建议(非标准)解决方案

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

enum class EnumClass { A, B = 4, C = 9, D = 60, E = 70 };
还假设此人忘记更新
checkEnumClass
的定义(这不太可能发生,尤其是当代码位于另一个文件中时)

将返回
false
,尽管70现在是一个有效值。单元测试可能有助于捕获此错误,但用户必须记住更新测试。(回想一下,他们一开始忘记更新代码!)

遗憾的是,标准C++没有提供一种方法来强制<代码>开关> <代码> > <代码>枚举> >代码>以覆盖所有的情况(与提供代码<最后的开关 >的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;
}
bool checkEnumClass(int v)
{
开关(静态_转换(v))
{
案例枚举类::A:
案例枚举类::B:
案例枚举类::C:
案例枚举类::D:
返回true;
}
返回false;
}
通过此解决方法,包含枚举器
E
但忘记相应更新
checkEnumClass
的人员将收到以下错误/警告:

通用条款:

警告:枚举值“E”未在开关[-Wswitch]中处理

Visual Studio:

错误C4062:未处理枚举“EnumClass”开关中的枚举数“E”
开关(静态_转换(v))

更新1:

作为一种良好的做法,在GCC的选项中添加
-Werror
,将所有警告转化为错误


更新2:优于
-Wswitch
的是将引发警告的
-Wswitch enum
(如果
-Werror
,则为错误)即使有默认的
标签。不幸的是,我不知道Visual Studio中有任何类似的功能。

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

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; };
template struct check_enum{static const bool value=false;};
模板结构检查\u枚举
{static const bool value=true;};
模板结构检查\u枚举
{static const bool value=true;};
模板结构检查\u枚举
{static const bool value=true;};
模板结构检查\u枚举
{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
static_assert(检查_enum::value,“无效的枚举值”);//确定!
静态断言(检查枚举::值,“无效枚举值”);//编译错误

编辑: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
template constepr bool check_enum=false;
模板constexpr bool check_enum=true;
模板constexpr bool check_enum=true;
模板constexpr bool check_enum=true;
模板constexpr bool check_enum=true;
静态断言(检查枚举,“无效枚举值”);//确定!
静态断言(检查枚举,“无效枚举值”);//编译错误

这些方法的主要缺点是专门化每个值的工作,您必须考虑这些工作是否值得。如果缺少一些值,则可能很难找到并解决问题。

我已编辑了该问题。如果枚举有一些“漏洞”更重要的是,我不能只说我为什么希望枚举包含洞?通常是在表示一个值列表(如状态代码等)的情况下然后,您只需要使用常量。枚举将呈现一些不连续的常量值。这不是我的选择。使用这种枚举这是一种糟糕的方法。@VaughnCato answer的前两行代码动态覆盖枚举的最小值和最大值。在您的代码中,当枚举更改时,检查为br
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