是否可以确定c ++枚举类的元素数量?

bqu*_*nin 64 c++ cardinality c++11 enum-class

是否有可能确定c ++的基数enum class:

enum class Example { A, B, C, D, E };
Run Code Online (Sandbox Code Playgroud)

sizeof但是,我尝试使用它返回枚举元素的大小.

sizeof(Example); // Returns 4 (on my architecture)
Run Code Online (Sandbox Code Playgroud)

有没有一种获得基数的标准方法(在我的例子中为5)?

Cam*_*ron 55

不是直接的,但你可以使用以下技巧:

enum class Example { A, B, C, D, E, Count };
Run Code Online (Sandbox Code Playgroud)

然后基数可用(int)Example::Count.

当然,如果你自动分配枚举值,从0开始,这只能很好地工作.如果不是这样,你可以手动为Count分配正确的基数,这与必须保持一个单独的常量没有什么不同无论如何:

enum class Example { A = 1, B = 2, C = 4, D = 8, E = 16, Count = 5 };
Run Code Online (Sandbox Code Playgroud)

一个缺点是编译器允许你Example::Count用作枚举值的参数 - 所以如果你使用它就要小心!(不过,我个人认为这在实践中并不是一个问题.)

  • 虽然如果将任何值设置为默认值以外的值,这将会中断.所以,要小心使用. (7认同)
  • 如果您使用带有此枚举的switch语句,任何体面的编译器都会警告您缺少一个案例.如果使用了很多可能非常烦人的东西..在这种特定情况下只有一个单独的变量可能会更好. (5认同)

Epo*_*ous 17

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;
Run Code Online (Sandbox Code Playgroud)

这是从UglyCoder的答案中得出的,但却以三种方式改进了它.

  • type_safe枚举(BEGINSIZE)中没有额外的元素(Cameron的答案也有这个问题.)
    • 编译器不会抱怨它们在switch语句中丢失(一个重大问题)
    • 它们不能无意中传递给期望你的枚举的功能.(不是常见问题)
  • 它不需要铸造使用.(卡梅隆的回答也有这个问题.)
  • 减法不会弄乱枚举类类型的大小.

它保留了UglyCoder相对于Cameron的答案优势,即可以为调查员分配任意值.

一个问题(与UglyCoder共享但不与Cameron共享)是它使换行符和注释显着......这是意料之外的.所以有人可以在不调整TEST_SIZE计算的情况下添加带有空格或注释的条目.

  • 像 clang-format 这样的工具也可以轻松打破这个问题 (2认同)

Nea*_*gye 11

对于C ++ 17,您可以magic_enum::enum_count从lib https://github.com/Neargye/magic_enum使用:

magic_enum::enum_count<Example>() -> 4。

  • 这太棒了!无需修改现有代码即可计算枚举成员的数量。而且这看起来实现得非常优雅(只是浏览了一下代码)! (2认同)
  • 一个巨大的限制是枚举值仅限于小范围(并且最多可以扩展到短范围,而不是 int 范围)。如果库失败,它不会因 static_assert 或编译时错误而失败,它只会“剪辑”枚举值并返回枚举值的子集。并不是说 C++ 可以做得更好,但有重要的限制。仍然受到支持,当我可以使用你的库时,它是我最喜欢的。 (2认同)

Ugl*_*der 7

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;
Run Code Online (Sandbox Code Playgroud)


Kir*_*nov 6

有一个基于 X()-macros: image 的技巧,你有以下枚举:

enum MyEnum {BOX, RECT};
Run Code Online (Sandbox Code Playgroud)

将其重新格式化为:

#define MyEnumDef \
    X(BOX), \
    X(RECT)
Run Code Online (Sandbox Code Playgroud)

然后下面的代码定义了枚举类型:

enum MyEnum
{
#define X(val) val
    MyEnumDef
#undef X
};
Run Code Online (Sandbox Code Playgroud)

以下代码计算枚举元素的数量:

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
...
Run Code Online (Sandbox Code Playgroud)


ixj*_*jxk 6

它可以通过 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__}); \
    }();                                                          \
};
Run Code Online (Sandbox Code Playgroud)

用法:

#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;
}
Run Code Online (Sandbox Code Playgroud)


dfr*_*fri 5

Reflection TS:枚举(和其他类型)的静态反射

\n

Reflection TS,特别是最新版本的 Reflection TS 草案的[reflect.ops.enum]/2提供了以下get_enumerators TransformationTrait操作:

\n
\n

[reflect.ops.enum]/2

\n
template <Enum T> struct get_enumerators\n
Run Code Online (Sandbox Code Playgroud)\n

所有专业化get_enumerators<T>均应满足要求TransformationTrait(20.10.1)。名为\n 的嵌套类型type指定满足\n 的元对象类型ObjectSequence,包含满足Enumerator并\n反映由 反映的枚举类型的枚举器的元素T

\n
\n

草案的 [reflect.ops.objseq] 涵盖了ObjectSequence操作,特别是 [reflect.ops.objseq]/1 涵盖了get_size提取满足以下条件的元对象的元素数量的特征ObjectSequence

\n
\n

[reflect.ops.objseq]/1

\n
template <ObjectSequence T> struct get_size;\n
Run Code Online (Sandbox Code Playgroud)\n

的所有特化get_size<T>均应满足UnaryTypeTrait基本特征 的要求 (20.10.1) integral_constant<size_t, N>,其中N是对象序列中元素的数量。

\n
\n

因此,在 Reflection TS 中,要以其当前形式接受和实现,可以在编译时计算枚举的元素数量,如下所示:

\n
enum class Example { A, B, C, D, E };\n\nusing ExampleEnumerators = get_enumerators<Example>::type;\n\nstatic_assert(get_size<ExampleEnumerators>::value == 5U, "");\n
Run Code Online (Sandbox Code Playgroud)\n

我们可能会在其中看到别名模板get_enumerators_vget_type_v进一步简化反射:

\n
enum class Example { A, B, C, D, E };\n\nusing ExampleEnumerators = get_enumerators_t<Example>;\n\nstatic_assert(get_size_v<ExampleEnumerators> == 5U, "");\n
Run Code Online (Sandbox Code Playgroud)\n
\n

反射 TS 的状态

\n

正如 Herb Sutter 的旅行报告所述:2018 年 6 月 9 日 ISO C++ 委员会夏季会议的夏季 ISO C++ 标准会议 (Rapperswil),Reflection TS 已被宣布为功能完整

\n
\n

Reflection TS 功能完整:Reflection TS 已被宣布功能完整,并将在夏季进行主要评论投票。再次注意,TS\xe2\x80\x99s 当前基于模板元编程的语法只是一个占位符;所请求的反馈是关于设计的核心 \xe2\x80\x9cguts\xe2\x80\x9d 的,委员会已经知道它打算用更简单的编程模型替换表面语法,该模型使用普通的编译时代码而不是<>风格的元编程。

\n
\n

最初计划用于 C++20,但目前还不清楚 Reflection TS 是否仍有机会进入 C++20 版本。

\n