是否有可能确定一个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,因此在编译时计算

但请注意,即使对于en
enum类
,默认初始化值也是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