是否可以确定 c++ 枚举类的元素数量?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14989274/
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
Is it possible to determine the number of elements of a c++ enum class?
提问by bquenin
Is it possible to determine the cardinality of a c++ enum class
:
是否可以确定 c++ 的基数enum class
:
enum class Example { A, B, C, D, E };
I tried to use sizeof
, however, it returns the size of an enum element.
我尝试使用sizeof
,但是,它返回枚举元素的大小。
sizeof(Example); // Returns 4 (on my architecture)
Is there a standard way to get the cardinality (5 in my example) ?
有没有标准的方法来获得基数(在我的例子中是 5)?
采纳答案by Cameron
Not directly, but you could use the following trick:
不是直接的,但您可以使用以下技巧:
enum class Example { A, B, C, D, E, Count };
Then the cardinality is available as (int)Example::Count
.
然后基数可用作(int)Example::Count
.
Of course, this only works nicely if you let values of the enum be automatically assigned, starting from 0. If that's not the case, you can manually assign the correct cardinality to Count, which is really no different from having to maintain a separate constant anyway:
当然,这只有在你让枚举的值从 0 开始自动分配的情况下才能很好地工作。如果不是这样,你可以手动为 Count 分配正确的基数,这与必须维护一个单独的常量没有什么不同反正:
enum class Example { A = 1, B = 2, C = 4, D = 8, E = 16, Count = 5 };
The one disadvantage is that the compiler will allow you to use Example::Count
as an argument for an enum value -- so be careful if you use this! (I personally find this not to be a problem in practice, though.)
一个缺点是编译器将允许您将其Example::Count
用作枚举值的参数——因此,如果您使用它,请务必小心!(不过,我个人认为这在实践中不是问题。)
回答by Eponymous
constexpr auto TEST_START_LINE = __LINE__;
enum class TEST { // Subtract extra lines from TEST_SIZE if an entry takes more than one
ONE = 7
, TWO = 6
, THREE = 9
};
constexpr auto TEST_SIZE = __LINE__ - TEST_START_LINE - 3;
This is derived from UglyCoder's answerbut improves it in three ways.
这源自UglyCoder 的答案,但在三个方面对其进行了改进。
- There are no extra elements in the type_safe enum (
BEGIN
andSIZE
) (Cameron's answeralso has this problem.)- The compiler will not complain about them being missing from a switch statement (a significant problem)
- They cannot be passed inadvertently to functions expecting your enum. (not a common problem)
- It does not require casting for use. (Cameron's answerhas this problem too.)
- The subtraction does not mess with the size of the enum class type.
- type_safe 枚举 (
BEGIN
和SIZE
)中没有额外的元素(卡梅伦的回答也有这个问题。)- 编译器不会抱怨 switch 语句中缺少它们(一个重大问题)
- 它们不能无意中传递给需要枚举的函数。(不是常见问题)
- 它不需要铸造使用。(卡梅伦的回答也有这个问题。)
- 减法不会弄乱枚举类类型的大小。
It retains UglyCoder'sadvantage over Cameron's answerthat enumerators can be assigned arbitrary values.
与Cameron 的答案相比,它保留了UglyCoder 的优势,即可以为枚举器分配任意值。
A problem (shared with UglyCoderbut not with Cameron) is that it makes newlines and comments significant ... which is unexpected. So someone could add an entry with whitespace or a comment without adjusting TEST_SIZE
's calculation.
一个问题(与UglyCoder共享但不与Cameron共享)是它使换行符和注释变得重要......这是出乎意料的。因此,有人可以添加带有空格或注释的条目,而无需调整TEST_SIZE
的计算。
回答by Neargye
For C++17 you can use magic_enum::enum_count
from lib https://github.com/Neargye/magic_enum:
对于 C++17,您可以magic_enum::enum_count
从 lib https://github.com/Neargye/magic_enum使用:
magic_enum::enum_count<Example>()
-> 4.
magic_enum::enum_count<Example>()
-> 4。
回答by UglyCoder
enum class TEST
{
BEGIN = __LINE__
, ONE
, TWO
, NUMBER = __LINE__ - BEGIN - 1
};
auto const TEST_SIZE = TEST::NUMBER;
// or this might be better
constexpr int COUNTER(int val, int )
{
return val;
}
constexpr int E_START{__COUNTER__};
enum class E
{
ONE = COUNTER(90, __COUNTER__) , TWO = COUNTER(1990, __COUNTER__)
};
template<typename T>
constexpr T E_SIZE = __COUNTER__ - E_START - 1;
回答by Kirill Suetnov
There is one trick based on X()-macros: image, you have the following enum:
有一个基于 X()-macros: image 的技巧,你有以下枚举:
enum MyEnum {BOX, RECT};
Reformat it to:
将其重新格式化为:
#define MyEnumDef \
X(BOX), \
X(RECT)
Then the following code defines enum type:
然后下面的代码定义了枚举类型:
enum MyEnum
{
#define X(val) val
MyEnumDef
#undef X
};
And the following code calculates number of enum elements:
以下代码计算枚举元素的数量:
template <typename ... T> void null(T...) {}
template <typename ... T>
constexpr size_t countLength(T ... args)
{
null(args...); //kill warnings
return sizeof...(args);
}
constexpr size_t enumLength()
{
#define XValue(val) #val
return countLength(MyEnumDef);
#undef XValue
}
...
std::array<int, enumLength()> some_arr; //enumLength() is compile-time
std::cout << enumLength() << std::endl; //result is: 2
...
回答by David Nehme
One trick you can try is to add a enum value at the end of your list and use that as the size. In your example
您可以尝试的一个技巧是在列表末尾添加一个枚举值并将其用作大小。在你的例子中
enum class Example { A, B, C, D, E, ExampleCount };
回答by David Nehme
No , you have to write it in the code.
不,你必须在代码中写它。
回答by arr_sea
If you make use of boost's preprocessor utilities, you can obtain the count using BOOST_PP_SEQ_SIZE(...)
.
如果您使用 boost 的预处理器实用程序,则可以使用BOOST_PP_SEQ_SIZE(...)
.
For example, one could define the CREATE_ENUM
macro as follows:
例如,可以CREATE_ENUM
如下定义宏:
#include <boost/preprocessor.hpp>
#define ENUM_PRIMITIVE_TYPE std::int32_t
#define CREATE_ENUM(EnumType, enumValSeq) \
enum class EnumType : ENUM_PRIMITIVE_TYPE \
{ \
BOOST_PP_SEQ_ENUM(enumValSeq) \
}; \
static constexpr ENUM_PRIMITIVE_TYPE EnumType##Count = \
BOOST_PP_SEQ_SIZE(enumValSeq); \
// END MACRO
Then, calling the macro:
然后,调用宏:
CREATE_ENUM(Example, (A)(B)(C)(D)(E));
would generate the following code:
将生成以下代码:
enum class Example : std::int32_t
{
A, B, C, D, E
};
static constexpr std::int32_t ExampleCount = 5;
This is only scratching the surface with regards to the boost preprocessor tools. For example, your macro could also define to/from string conversion utilities and ostream operators for your strongly typed enum.
这只是关于增强预处理器工具的皮毛。例如,您的宏还可以为您的强类型枚举定义 to/from 字符串转换实用程序和 ostream 运算符。
More on boost preprocessor tools here: https://www.boost.org/doc/libs/1_70_0/libs/preprocessor/doc/AppendixA-AnIntroductiontoPreprocessorMetaprogramming.html
有关 boost 预处理器工具的更多信息,请访问:https: //www.boost.org/doc/libs/1_70_0/libs/preprocessor/doc/AppendixA-AnIntroductiontoPreprocessorMetaprogramming.html
As an aside, I happen to strongly agree with @FantasticMrFox that the additional Count
enumerated value employed in the accepted answer will create compiler warning headaches galore if using a switch
statement. I find the unhandled case
compiler warning quite useful for safer code maintenance, so I wouldn't want to undermine it.
顺便说一句,我碰巧非常同意@FantasticMrFox,Count
如果使用switch
语句,在接受的答案中使用的附加枚举值会造成编译器警告问题。我发现unhandled case
编译器警告对于更安全的代码维护非常有用,所以我不想破坏它。
回答by ixjxk
It can be solved by a trick with std::initializer_list:
它可以通过 std::initializer_list 的技巧解决:
#define TypedEnum(Name, Type, ...) \
struct Name { \
enum : Type{ \
__VA_ARGS__ \
}; \
static inline const size_t count = []{ \
static Type __VA_ARGS__; return std::size({__VA_ARGS__}); \
}(); \
};
Usage:
用法:
#define Enum(Name, ...) TypedEnum(Name, int, _VA_ARGS_)
Enum(FakeEnum, A = 1, B = 0, C)
int main()
{
std::cout << FakeEnum::A << std::endl
<< FakeEnun::count << std::endl;
}
回答by MetalTurnip
There is another way that doesn't rely on line counts or templates. The only requirement is sticking the enum values in their own file and making the preprocessor/compiler do the count like so:
还有另一种不依赖于行数或模板的方法。唯一的要求是将枚举值粘贴在它们自己的文件中,并使预处理器/编译器像这样进行计数:
my_enum_inc.h
my_enum_inc.h
ENUMVAL(BANANA)
ENUMVAL(ORANGE=10)
ENUMVAL(KIWI)
...
#undef ENUMVAL
my_enum.h
my_enum.h
typedef enum {
#define ENUMVAL(TYPE) TYPE,
#include "my_enum_inc.h"
} Fruits;
#define ENUMVAL(TYPE) +1
const size_t num_fruits =
#include "my_enum_inc.h"
;
This allows you to put comments with the enum values, re-assign values and does not inject an invalid 'count' enum value that needs to be ignored/accounted for in code.
这允许您对枚举值添加注释,重新分配值,并且不会注入需要在代码中忽略/考虑的无效“计数”枚举值。
If you don't care about comments you don't need an extra file and can do as someone above mentioned, e.g.:
如果你不关心评论,你不需要额外的文件,可以像上面提到的那样做,例如:
#define MY_ENUM_LIST \
ENUMVAL(BANANA) \
ENUMVAL(ORANGE = 7) \
ENUMVAL(KIWI)
and replace the #include "my_enum_inc.h"
directives with MY_ENUM_LIST but you'll need to #under ENUMVAL
after each use.
并#include "my_enum_inc.h"
用 MY_ENUM_LIST替换指令,但#under ENUMVAL
每次使用后都需要。