是否有可能确定一个c++;枚举类? 是否可以确定C++ +>代码>枚举类< /代码>: enum class Example { A, B, C, D, E };
我尝试使用是否有可能确定一个c++;枚举类? 是否可以确定C++ +>代码>枚举类< /代码>: enum class Example { A, B, C, D, E };,c++,c++11,cardinality,enum-class,C++,C++11,Cardinality,Enum Class,我尝试使用sizeof,但是,它返回枚举元素的大小 sizeof(Example); // Returns 4 (on my architecture) 是否有一种标准的方法来获取基数(在我的示例中为5)?不,您必须在代码中编写它。不直接,但您可以使用以下技巧: enum class Example { A, B, C, D, E, Count }; 然后基数可以作为static\u cast(示例::Count)使用 当然,这只有在允许从0开始自动分配枚举值的情况下才能很好地工作。如果不是
sizeof
,但是,它返回枚举元素的大小
sizeof(Example); // Returns 4 (on my architecture)
是否有一种标准的方法来获取基数(在我的示例中为5)?不,您必须在代码中编写它。不直接,但您可以使用以下技巧:
enum class Example { A, B, C, D, E, Count };
然后基数可以作为static\u cast(示例::Count)
使用
当然,这只有在允许从0开始自动分配枚举值的情况下才能很好地工作。如果不是这样,您可以手动为Count分配正确的基数,这与无论如何都必须保持一个单独的常量没有什么区别:
enum class Example { A = 1, B = 2, C = 4, D = 8, E = 16, Count = 5 };
一个缺点是编译器将允许您使用
Example::Count
作为枚举值的参数,因此使用此参数时要小心!(不过,我个人认为这在实践中不是问题。)您可以尝试的一个技巧是在列表末尾添加一个枚举值,并将其用作大小。在你的例子中
enum class Example { A, B, C, D, E, ExampleCount };
//关闭叮当格式
枚举类测试
{
开始=\u行__
一
二
,NUMBER=\uuuuu行\uuuu-BEGIN-1
};
//叮当作响
自动常量测试大小=测试::编号;
//或者这可能更好
constexpr int计数器(int val,int)
{
返回val;
}
constexpr int E_START{u__计数器};
枚举类E
{
一个=计数器(90,计数器),两个=计数器(1990,计数器)
};
模板
constexpr T E_SIZE=_计数器\uuuu-E_开始-1;
这是从中衍生出来的,但从三个方面进行了改进
- type_安全枚举(
和BEGIN
)中没有额外的元素(也有此问题。)SIZE
- 编译器不会抱怨switch语句中缺少它们(一个重大问题)
- 它们不能无意中传递给需要枚举的函数。(不是常见问题)
- 使用时不需要浇铸。(也有这个问题。)
- 减法不会影响枚举类类型的大小
一个问题(与共享但不与共享)是它使换行和注释变得重要。。。这是出乎意料的。因此,有人可以添加带有空格的条目或注释,而无需调整
TEST\u SIZE
的计算。这意味着代码格式化程序可以打破这一点。在注释之后,我编辑了禁用<代码> CLAN格式< /C> >,但如果使用不同的格式化程序,则省略。 < p>也可以考虑<代码> StasyType(示例::e)+ 1 < /COD>这消除了额外的元素。 < P>有一个基于X()的技巧:宏:图像,您有以下枚举:
enum MyEnum {BOX, RECT};
将其重新格式化为:
#define MyEnumDef \
X(BOX), \
X(RECT)
然后,以下代码定义枚举类型:
enum MyEnum
{
#define X(val) val
MyEnumDef
#undef X
};
下面的代码计算枚举元素的数量:
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
...
template void null(T...{}
模板
constexpr size\u t countLength(t…args)
{
null(args…)//终止警告
返回…的大小(args);
}
constexpr size_t enumLength()
{
#定义XValue(val)#val
返回countLength(MyEnumDef);
#未定义XValue
}
...
std::数组一些数组//enumLength()是编译时
std::cout如果使用boost的预处理器实用程序,可以使用boost\u PP\u SEQ\u SIZE(…)
获得计数
例如,可以如下定义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
enum class Example { A, B, C, D, E };
using ExampleEnumerators = get_enumerators<Example>::type;
static_assert(get_size<ExampleEnumerators>::value == 5U, "");
将生成以下代码:
enum class Example : std::int32_t
{
A, B, C, D, E
};
static constexpr std::int32_t ExampleCount = 5;
对于boost预处理器工具来说,这只是表面现象。例如,宏还可以为强类型枚举定义到/从字符串转换实用程序和ostream运算符
有关boost预处理器工具的更多信息,请参见:
顺便说一句,我非常同意@FantasticMrFox的观点,即如果使用switch
语句,则接受答案中使用的额外计数
枚举值将产生大量编译器警告问题。我发现unhandled case
编译器警告对于更安全的代码维护非常有用,所以我不想破坏它。对于C++17,您可以使用库中的magic_enum::enum_count
:
magic_enum::enum_count()
->4
缺点在哪里?
此库使用编译器特定的hack(基于\uuuu PRETTY\u FUNCTION\uuuu
/\uuuuu FUNCSIG\uuuuu
),它适用于Clang>=5、MSVC>=15.3和GCC>=9
我们遍历给定的区间范围,找到所有具有名称的枚举,这将是它们的计数。
阅读更多关于
在这篇文章中有更多关于这个黑客的信息。它可以通过std::initializer\u 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__}); \
}(); \
};
用法:
#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;
}
#定义枚举(名称,…)类型数(名称,整数,_VA_ARGS)
枚举(FakeEnum,A=1,B=0,C)
int main()
{
std::cout还有另一种方法不依赖于行计数或模板。唯一的要求是将枚举值粘贴到它们自己的文件中,并使预处理器/编译器像这样进行计数:
my_enum_inc.h
ENUMVAL(BANANA)
ENUMVAL(ORANGE=10)
ENUMVAL(KIWI)
...
#undef ENUMVAL
我的_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"
;
这允许您使用枚举值放置注释,重新分配值,并且不会插入需要在代码中忽略/说明的无效“count”枚举值
如果你不在乎评论,你不需要额外的文件,可以像上面提到的那样做,例如:
#define MY_ENUM_LIST \
ENUMVAL(BANANA) \
ENUMVAL(ORANGE = 7) \
ENUMVAL(KIWI)
并用my_enum_列表替换#include“my_enum_inc.h”
指令,但每次使用后您都需要#取消ENUMVAL
。另一种“愚蠢”的解决方案是:
enum class Example { A, B, C, D, E };
constexpr int ExampleCount = [] {
Example e{};
int count = 0;
switch (e) {
case Example::A:
count++;
case Example::B:
count++;
case Example::C:
count++;
case Example::D:
count++;
case Example::E:
count++;
}
return count;
}();
通过使用-Werror=switch
编译此代码,如果省略或复制任何开关大小写,则确保得到编译器警告。它也是constexpr,因此在编译时计算
但请注意,即使对于enenum类
,默认初始化值也是0,即使enum的第一个值是no
#define MY_ENUM_LIST \
ENUMVAL(BANANA) \
ENUMVAL(ORANGE = 7) \
ENUMVAL(KIWI)
enum class Example { A, B, C, D, E };
constexpr int ExampleCount = [] {
Example e{};
int count = 0;
switch (e) {
case Example::A:
count++;
case Example::B:
count++;
case Example::C:
count++;
case Example::D:
count++;
case Example::E:
count++;
}
return count;
}();
template <Enum T> struct get_enumerators
template <ObjectSequence T> struct get_size;
enum class Example { A, B, C, D, E };
using ExampleEnumerators = get_enumerators<Example>::type;
static_assert(get_size<ExampleEnumerators>::value == 5U, "");
enum class Example { A, B, C, D, E };
using ExampleEnumerators = get_enumerators_t<Example>;
static_assert(get_size_v<ExampleEnumerators> == 5U, "");
#define Enum(Name, ...) \
struct Name { \
enum : int { \
__VA_ARGS__ \
}; \
private: struct en_size { int __VA_ARGS__; }; \
public: static constexpr size_t count = sizeof(en_size)/sizeof(int); \
}
struct S {
Enum(TestEnum, a=11, b=22, c=33);
void Print() {
std::cout << TestEnum::a << '\n';
std::cout << TestEnum::b << '\n';
std::cout << TestEnum::count << '\n';
}
};
int main()
{
S d;
d.Print();
return 0
}
11
22
3