是否有可能确定c ++的基数enum class
:
enum class Example { A, B, C, D, E };
我尝试使用sizeof
,但是它返回枚举元素的大小。
sizeof(Example); // Returns 4 (on my architecture)
是否有获取基数的标准方法(在我的示例中为5)?
是否有可能确定c ++的基数enum class
:
enum class Example { A, B, C, D, E };
我尝试使用sizeof
,但是它返回枚举元素的大小。
sizeof(Example); // Returns 4 (on my architecture)
是否有获取基数的标准方法(在我的示例中为5)?
enum
和enum class
es是非常不同的概念。
enum class
值,那么知道这个数字会有什么好处呢?
Answers:
不直接,但是您可以使用以下技巧:
enum class Example { A, B, C, D, E, Count };
然后,基数可作为static_cast<int>(Example::Count)
。
当然,只有在从0开始自动分配枚举值的情况下,这种方法才能很好地工作。无论如何:
enum class Example { A = 1, B = 2, C = 4, D = 8, E = 16, Count = 5 };
一个缺点是编译器将允许您将其Example::Count
用作枚举值的参数-如果使用此值,请务必小心!(不过,我个人认为这实际上不是问题。)
enum class
es相比而不是plain enum
s有点混乱。为了清楚起见,我将进行演员表编辑。
对于C ++ 17,您可以magic_enum::enum_count
从lib https://github.com/Neargye/magic_enum使用:
magic_enum::enum_count<Example>()
-> 4。
该库使用特定于编译器的黑客(基于__PRETTY_FUNCTION__
/ __FUNCSIG__
),可在Clang> = 5,MSVC> = 15.3和GCC> = 9上工作。
我们遍历给定的间隔范围,并找到所有带有名称的枚举,这将是它们的计数。阅读有关限制的更多信息
有关此黑客的更多信息,请参见https://taylorconor.com/blog/enum-reflection。
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;
这是从UglyCoder的答案中得出的,但是通过三种方式对其进行了改进。
BEGIN
和SIZE
)中没有多余的元素(Cameron的答案也有此问题。)
与Cameron的答案相比,它保留了UglyCoder的优势,即枚举数可以分配任意值。
问题(与UglyCoder共享但与Cameron共享)是它使换行符和注释变得重要……这是意外的。因此,有人可以添加带有空格或注释的条目,而无需调整TEST_SIZE
的计算。
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;
short
可能会被破坏,int
例如在进行统一构建时。(不过,我认为这是统一构建的问题,而不是您建议的技巧。)
有一个基于X()-macros的技巧:image,您具有以下枚举:
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
...
#define MyEnumDef
(并将其放入#define X(val) val
),可以使此操作变得更加容易,这使您可以使用just来计数元素的数量#define X(val) +1
constexpr std::size_t len = MyEnumDef;
。
如果您使用boost的预处理器实用程序,则可以使用获得计数BOOST_PP_SEQ_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
然后,调用宏:
CREATE_ENUM(Example, (A)(B)(C)(D)(E));
将生成以下代码:
enum class Example : std::int32_t
{
A, B, C, D, E
};
static constexpr std::int32_t ExampleCount = 5;
关于升压预处理器工具,这只是擦伤表面。例如,您的宏还可以为强类型枚举定义往返字符串转换实用程序和ostream运算符。
有关升压预处理器工具的更多信息,请访问:https : //www.boost.org/doc/libs/1_70_0/libs/preprocessor/doc/AppendixA-AnIntroductiontoPreprocessorMetaprogramming.html
顺便说一句,我碰巧非常同意@FantasticMrFox,即Count
如果使用switch
语句,那么在接受的答案中使用的其他枚举值将引起编译器警告,令人头疼。我发现unhandled case
编译器警告对于更安全的代码维护非常有用,因此我不想破坏它。
可以通过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__}); \
}(); \
};
用法:
#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;
}
还有另一种不依赖行数或模板的方式。唯一的要求是将枚举值粘贴在自己的文件中,并使预处理器/编译器进行计数,如下所示:
my_enum_inc.h
ENUMVAL(BANANA)
ENUMVAL(ORANGE=10)
ENUMVAL(KIWI)
...
#undef ENUMVAL
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"
;
这样,您就可以使用枚举值放置注释,重新分配值,并且不会注入需要在代码中忽略/解释的无效“计数”枚举值。
如果您不在乎评论,则不需要额外的文件,可以像上面提到的那样操作,例如:
#define MY_ENUM_LIST \
ENUMVAL(BANANA) \
ENUMVAL(ORANGE = 7) \
ENUMVAL(KIWI)
并#include "my_enum_inc.h"
用MY_ENUM_LIST替换指令,但#undef 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 class
,即使枚举的第一个值不为0,默认的初始化值也为0。因此,您必须从0开始或显式使用第一个值。
不,您必须在代码中编写它。
您也可以考虑static_cast<int>(Example::E) + 1
消除多余的元素。
Example::E
为枚举中的最后一个值。即使不是这种情况,其Example::E
字面值也可能会更改。
Reflection TS,尤其是Reflection TS草案的最新版本的[reflect.ops.enum] / 2提供了以下get_enumerators
TransformationTrait
操作:
[reflect.ops.enum] / 2
template <Enum T> struct get_enumerators
的所有专业
get_enumerators<T>
应符合TransformationTrait
要求(20.10.1)。名为的嵌套类型type
表示满足的元对象类型ObjectSequence
,其中包含满足Enumerator
并反映由反映的枚举类型的枚举数的元素T
。
草案的[reflect.ops.objseq]涵盖了ObjectSequence
操作,特别是[reflect.ops.objseq] / 1涵盖了get_size
为满足以下条件的元对象提取元素数量的特征ObjectSequence
:
[reflect.ops.objseq] / 1
template <ObjectSequence T> struct get_size;
的所有专业
get_size<T>
应满足UnaryTypeTrait
要求(20.10.1),其基本特征为integral_constant<size_t, N>
,其中N
是对象序列中元素的数量。
因此,在Reflection TS以其当前形式被接受和实现时,可以在编译时计算枚举的元素数,如下所示:
enum class Example { A, B, C, D, E };
using ExampleEnumerators = get_enumerators<Example>::type;
static_assert(get_size<ExampleEnumerators>::value == 5U, "");
在这里我们可能会看到别名模板get_enumerators_v
并get_type_v
进一步简化反射:
enum class Example { A, B, C, D, E };
using ExampleEnumerators = get_enumerators_t<Example>;
static_assert(get_size_v<ExampleEnumerators> == 5U, "");
正如Herb Sutter的旅行报告: 2018年6月9日夏季ISO C ++委员会夏季会议的夏季ISO C ++标准会议(Rapperswil)所述,Reflection TS已被宣布为功能完善的
Reflection TS已完成功能:Reflection TS已被宣布为功能已完成,并将在今年夏天进行主要评论投票。再次注意,TS当前基于模板的基于元编程的语法只是一个占位符;所要求的反馈意见是在设计的核心“要点”上,并且委员会已经知道打算用一种简单的编程模型代替表面语法,该模型使用普通的编译时代码而不是
<>
样式化元编程。
并初步刨为C ++ 20,但它有点不清楚,如果反射TS仍然有机会使其进入C ++ 20日发布。