是否有可能确定 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 class Example { A, B, C, D, E, Count };
然后可以使用static_cast<int>(Example::Count)
获得枚举元素的基数。
当然,这仅在您允许自动分配枚举值(从0开始)时有效。如果不是这种情况,则可以手动将正确的基数分配给Count,这与维护一个单独的常量没有什么区别:
enum class Example { A = 1, B = 2, C = 4, D = 8, E = 16, Count = 5 };
唯一的劣势是编译器会允许您将 Example::Count
用作枚举值的参数,因此如果您使用它,请小心!(尽管我个人认为在实践中这不是一个问题。)
enum class
而不是普通的enum
会使这个技巧变得有些混乱。我会编辑并添加一个强制转换以便更清晰明了。 - Cameron对于C++17,您可以使用来自库https://github.com/Neargye/magic_enum的magic_enum::enum_count
:
magic_enum::enum_count<Example>()
-> 4.
该库使用编译器特定的技巧(基于__PRETTY_FUNCTION__
/ __FUNCSIG__
),它适用于Clang >= 5、MSVC >= 15.3和GCC >= 9。
我们遍历所给区间范围,并找到所有具有名称的枚举,这将是它们的计数。 阅读更多关于限制的信息。
在此帖子https://taylorconor.com/blog/enum-reflection中了解更多关于这种技巧的信息。
-ftime-trace
来报告constexpr代码时,并没有报告太多信息:https://github.com/llvm/llvm-project/issues/42754 - Trass3r// clang-format off
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;
// clang-format on
这段内容源自于UglyCoder的回答,但是在以下三个方面进行了改进:
BEGIN
和SIZE
)(Cameron的回答也存在此问题)。
它保留了UglyCoder的优点,即枚举器可以被分配任意值,而Cameron的回答则没有。
一个问题(与UglyCoder共享,但与Cameron不共享)是它使换行和注释具有重要意义...这是出乎意料的。因此,有人可能会添加带有空格或注释的条目,而不调整TEST_SIZE
的计算。这意味着代码格式化程序可能会破坏此功能。在evg656e's的评论之后,我编辑了答案以禁用clang-format
,但如果您使用不同的格式化程序,则需要自行注意。clang-format
添加了一个解决方法,并明确提到代码格式化器作为添加空格问题的一部分。 - Eponymous// clang-format off
enum class TEST
{
BEGIN = __LINE__
, ONE
, TWO
, NUMBER = __LINE__ - BEGIN - 1
};
// clang-format on
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
的枚举类型,在进行 Unity 构建时也可能会升级到 int
。 (我认为这更多是 Unity 构建的问题,而不是您提出的技巧的问题。) - Kyle Strand您可以通过使用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;
}
有一种基于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
...
#define MyEnumDef
中删除逗号(并将其放在 #define X(val) val
中),可以使此过程更加简单,这样您就可以仅使用 #define X(val) +1
constexpr std::size_t len = MyEnumDef;
来计算元素的数量。 - HolyBlackCat反射TS,尤其是最新版本的反射TS草案中的[reflect.ops.enum]/2提供了get_enumerators
TransformationTrait
操作:
[reflect.ops.enum]/2
template <Enum T> struct get_enumerators
All specializations of
get_enumerators<T>
shall meet theTransformationTrait
requirements (20.10.1). The nested type namedtype
designates a meta-object type satisfyingObjectSequence
, containing elements which satisfyEnumerator
and reflect the enumerators of the enumeration type reflected byT
.
[reflect.ops.objseq]草案涵盖了ObjectSequence
操作,特别是[reflect.ops.objseq]/1涵盖了get_size
特性,用于提取满足ObjectSequence
的元对象的元素数量:
因此,如果Reflection TS以其当前形式被接受和实现,枚举的元素数量可以在编译时计算,如下所示:[reflect.ops.objseq]/1
template <ObjectSequence T> struct get_size;
All specializations of
get_size<T>
shall meet theUnaryTypeTrait
requirements (20.10.1) with a base characteristic ofintegral_constant<size_t, N>
, whereN
is the number of elements in the object sequence.
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++委员会夏季会议的Trip report: Summer ISO C++ standards meeting (Rapperswil)中所述,Reflection TS已被宣布为功能完整。
Reflection TS是功能完整的:Reflection TS已被宣布为功能完整,并将在今年夏天进行主要评论投票。请注意,TS的当前模板元编程语法只是占位符;请求反馈的重点在于设计的核心“内涵”,委员会已经知道打算用普通的编译时代码而不是
<>
风格的元编程来替换表面语法。
最初计划将其纳入C++20,但目前尚不确定Reflection TS是否仍有机会进入C++20发布。
你可以尝试的一个技巧是在列表末尾添加一个枚举值,并将其用作大小。 在你的例子中
enum class Example { A, B, C, D, E, ExampleCount };
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;
Count
枚举值将在使用switch
语句时创建编译器警告头痛。我发现未处理的情况
编译器警告对于更安全的代码维护非常有用,因此我不想削弱它。
enum
和enum class
是非常不同的概念。 - Shoe