C++ 如何检查枚举值是否有效?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4969233/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to check if enum value is valid?
提问by B?ови?
I am reading an enum
value from a binary file and would like to check if the value is really part of the enum
values. How can I do it?
我正在enum
从二进制文件中读取一个值,并想检查该值是否真的是enum
值的一部分。我该怎么做?
#include <iostream>
enum Abc
{
A = 4,
B = 8,
C = 12
};
int main()
{
int v1 = 4;
Abc v2 = static_cast< Abc >( v1 );
switch ( v2 )
{
case A:
std::cout<<"A"<<std::endl;
break;
case B:
std::cout<<"B"<<std::endl;
break;
case C:
std::cout<<"C"<<std::endl;
break;
default :
std::cout<<"no match found"<<std::endl;
}
}
Do I have to use the switch
operator or is there a better way?
我必须使用switch
运算符还是有更好的方法?
EDIT
编辑
I have enum values set and unfortunately I can not modify them. To make things worse, they are not continuous (their values goes 0, 75,76,80,85,90,95,100, etc.)
我设置了枚举值,不幸的是我无法修改它们。更糟糕的是,它们不是连续的(它们的值变为 0、75、76、80、85、90、95、100 等)
采纳答案by Leonid
enum
value is valid in C++ if it falls in range [A, B], which is defined by the standard rule below. So in case of enum X { A = 1, B = 3 }
, the value of 2
is considered a valid enum value.
enum
如果 value 在范围 [A, B] 内,则该值在 C++ 中有效,该范围由以下标准规则定义。因此,在 的情况下enum X { A = 1, B = 3 }
, 的值2
被视为有效的枚举值。
Consider 7.2/6 of standard:
考虑标准的 7.2/6:
For an enumeration where emin is the smallest enumerator and emax is the largest, the values of the enumeration are the values of the underlying type in the range bmin to bmax, where bmin and bmax are, respectively, the smallest and largest values of the smallest bit-field that can store emin and emax. It is possible to define an enumeration that has values not defined by any of its enumerators.
对于 emin 是最小枚举数而 emax 是最大枚举数的枚举,枚举值是 bmin 到 bmax 范围内的基础类型的值,其中 bmin 和 bmax 分别是最小值的最小值和最大值可以存储 emin 和 emax 的位域。可以定义一个枚举,它的值不是由它的任何枚举器定义的。
There is no retrospection in C++. One approach to take is to list enum values in an array additionally and write a wrapper that would do conversion and possibly throw an exception on failure.
C++ 中没有回顾。一种方法是在数组中额外列出枚举值并编写一个包装器来进行转换并可能在失败时抛出异常。
See Similar Questionabout how to cast int to enum for further details.
有关更多详细信息,请参阅有关如何将 int 转换为 enum 的类似问题。
回答by Andrew
Maybe use enum like this:
也许像这样使用枚举:
enum MyEnum
{
A,
B,
C
};
and to check
并检查
if (v2 >= A && v2 <= C)
If you don't specify values for enum constants, the values start at zero and increase by one with each move down the list. For example, given
enum MyEnumType { ALPHA, BETA, GAMMA };
ALPHA has a value of 0, BETA has a value of 1, and GAMMA has a value of 2.
如果您没有为枚举常量指定值,则这些值从零开始,并在列表中每向下移动一次就增加一。例如,给定
enum MyEnumType { ALPHA, BETA, GAMMA };
ALPHA 的值为 0,BETA 的值为 1,GAMMA 的值为 2。
回答by janm
In C++ 11 there is a better way if you are prepared to list your enum values as template parameters. You can look at this as a good thing, allowing you to accept subsets of the valid enum values in different contexts; often useful when parsing codes from external sources.
在 C++ 11 中,如果您准备将枚举值列为模板参数,则有更好的方法。您可以将其视为一件好事,允许您在不同的上下文中接受有效枚举值的子集;在解析来自外部来源的代码时通常很有用。
A possible useful addition to the example below would be some static assertions around the underlying type of EnumType relative to IntType to avoid truncation issues. Left as an exercise.
下面示例的一个可能有用的补充是围绕 EnumType 的基础类型相对于 IntType 的一些静态断言,以避免截断问题。留下作为练习。
#include <stdio.h>
template<typename EnumType, EnumType... Values> class EnumCheck;
template<typename EnumType> class EnumCheck<EnumType>
{
public:
template<typename IntType>
static bool constexpr is_value(IntType) { return false; }
};
template<typename EnumType, EnumType V, EnumType... Next>
class EnumCheck<EnumType, V, Next...> : private EnumCheck<EnumType, Next...>
{
using super = EnumCheck<EnumType, Next...>;
public:
template<typename IntType>
static bool constexpr is_value(IntType v)
{
return v == static_cast<IntType>(V) || super::is_value(v);
}
};
enum class Test {
A = 1,
C = 3,
E = 5
};
using TestCheck = EnumCheck<Test, Test::A, Test::C, Test::E>;
void check_value(int v)
{
if (TestCheck::is_value(v))
printf("%d is OK\n", v);
else
printf("%d is not OK\n", v);
}
int main()
{
for (int i = 0; i < 10; ++i)
check_value(i);
}
回答by Matthieu M.
The only way I ever found to make it 'easy', was to create (macro) a sorted array of the enums and checking with that.
我发现让它“容易”的唯一方法是创建(宏)一个排序的枚举数组并检查它。
The switch
trick fail with enum
s because an enum
may have more than one enumerator with a given value.
这个switch
技巧会以enum
s失败,因为enum
一个给定值可能有多个枚举器。
It's an annoying issue, really.
这是一个烦人的问题,真的。
回答by Brett
Managed Extensions for C++ supports the following syntax:
C++ 的托管扩展支持以下语法:
enum Abc
{
A = 4,
B = 8,
C = 12
};
Enum::IsDefined(Abc::typeid, 8);
Reference: MSDN "Managed Extensions for C++ Programming"
参考:MSDN“ C++ 编程的托管扩展”
回答by Alex Zaharov
Kinda necro, but ... makes a RANGE check of int into first/last enum values (can be combined with janm's idea to make exact checks), C++11:
有点坏死,但是......对 int 进行范围检查到第一个/最后一个枚举值(可以结合 janm 的想法进行精确检查),C++11:
Header:
标题:
namespace chkenum
{
template <class T, T begin, T end>
struct RangeCheck
{
private:
typedef typename std::underlying_type<T>::type val_t;
public:
static
typename std::enable_if<std::is_enum<T>::value, bool>::type
inrange(val_t value)
{
return value >= static_cast<val_t>(begin) && value <= static_cast<val_t>(end);
}
};
template<class T>
struct EnumCheck;
}
#define DECLARE_ENUM_CHECK(T,B,E) namespace chkenum {template<> struct EnumCheck<T> : public RangeCheck<T, B, E> {};}
template<class T>
inline
typename std::enable_if<std::is_enum<T>::value, bool>::type
testEnumRange(int val)
{
return chkenum::EnumCheck<T>::inrange(val);
}
Enum declaration:
枚举声明:
enum MinMaxType
{
Max = 0x800, Min, Equal
};
DECLARE_ENUM_CHECK(MinMaxType, MinMaxType::Max, MinMaxType::Equal);
Usage:
用法:
bool r = testEnumRange<MinMaxType>(i);
Mainly difference of above proposed it that test function is dependent on enum type itself only.
上面提出的主要区别在于测试函数仅依赖于枚举类型本身。
回答by Suma
Speaking about a language, there is no better way, the enum values exist compile time only and there is no way to enumerate them programatically. With a well thought infrastructure you may still be able to avoid listing all values several times, though. See Easy way to use variables of enum types as string in C?
说到语言,没有更好的方法,枚举值仅在编译时存在,无法以编程方式枚举它们。不过,有了经过深思熟虑的基础架构,您仍然可以避免多次列出所有值。请参阅在 C 中将枚举类型的变量用作字符串的简单方法?
Your sample can then be rewritten using the "enumFactory.h" provided there as:
然后可以使用那里提供的“enumFactory.h”重写您的示例:
#include "enumFactory.h"
#define ABC_ENUM(XX) \
XX(A,=4) \
XX(B,=8) \
XX(C,=12) \
DECLARE_ENUM(Abc,ABC_ENUM)
int main()
{
int v1 = 4;
Abc v2 = static_cast< Abc >( v1 );
#define CHECK_ENUM_CASE(name,assign) case name: std::cout<< #name <<std::endl; break;
switch ( v2 )
{
ABC_ENUM(CHECK_ENUM_CASE)
default :
std::cout<<"no match found"<<std::endl;
}
#undef CHECK_ENUM_CASE
}
or even (using some more facilities already existing in that header):
甚至(使用该标头中已经存在的更多功能):
#include "enumFactory.h"
#define ABC_ENUM(XX) \
XX(A,=4) \
XX(B,=8) \
XX(C,=12) \
DECLARE_ENUM(Abc,ABC_ENUM)
DEFINE_ENUM(Abc,ABC_ENUM)
int main()
{
int v1 = 4;
Abc v2 = static_cast< Abc >( v1 );
const char *name = GetString(v2);
if (name[0]==0) name = "no match found";
std::cout << name << std::endl;
}
回答by Innocent Bystander
Yet another way to do it:
另一种方法来做到这一点:
#include <algorithm>
#include <iterator>
#include <iostream>
template<typename>
struct enum_traits { static constexpr void* values = nullptr; };
namespace detail
{
template<typename T>
constexpr bool is_value_of(int, void*) { return false; }
template<typename T, typename U>
constexpr bool is_value_of(int v, U)
{
using std::begin; using std::end;
return std::find_if(begin(enum_traits<T>::values), end(enum_traits<T>::values),
[=](auto value){ return value == static_cast<T>(v); }
) != end(enum_traits<T>::values);
}
}
template<typename T>
constexpr bool is_value_of(int v)
{ return detail::is_value_of<T>(v, decltype(enum_traits<T>::values) { }); }
////////////////////
enum Abc { A = 4, B = 8, C = 12 };
template<>
struct enum_traits<Abc> { static constexpr auto values = { A, B, C }; };
decltype(enum_traits<Abc>::values) enum_traits<Abc>::values;
enum class Def { D = 1, E = 3, F = 5 };
int main()
{
std::cout << "Abc:";
for(int i = 0; i < 10; ++i)
if(is_value_of<Abc>(i)) std::cout << " " << i;
std::cout << std::endl;
std::cout << "Def:";
for(int i = 0; i < 10; ++i)
if(is_value_of<Def>(i)) std::cout << " " << i;
std::cout << std::endl;
return 0;
}
The "ugly" part of this approach IMHO is having to define:
恕我直言,这种方法的“丑陋”部分必须定义:
decltype(enum_traits<Abc>::values) enum_traits<Abc>::values
If you are not opposed to macros, you can wrap it inside a macro:
如果您不反对宏,则可以将其包装在宏中:
#define REGISTER_ENUM_VALUES(name, ...) \
template<> struct enum_traits<name> { static constexpr auto values = { __VA_ARGS__ }; }; \
decltype(enum_traits<name>::values) enum_traits<name>::values;