是否可以确定c ++ enum class
的基数:
enum class Example { A, B, C, D, E };
我尝试使用sizeof
,但是,它返回枚举元素的大小。
sizeof(Example); // Returns 4 (on my architecture)
是否有获得基数的标准方法(在我的示例中为5)?
答案 0 :(得分:52)
不是直接的,但您可以使用以下技巧:
enum class Example { A, B, C, D, E, Count };
然后基数可以(int)Example::Count
。
当然,如果你从0开始自动分配枚举的值,这只能很好地工作。如果不是这样的话,你可以手动将正确的基数分配给Count,这与必须保持不同一个单独的常数:
enum class Example { A = 1, B = 2, C = 4, D = 8, E = 16, Count = 5 };
一个缺点是编译器允许你使用Example::Count
作为枚举值的参数 - 所以如果你使用它就要小心! (不过,我个人认为这在实践中并不是一个问题。)
答案 1 :(得分:15)
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's answer但以三种方式改进。
BEGIN
和SIZE
)(Cameron's answer也存在此问题。)
UglyCoder's优于Cameron's answer优势,可以为枚举数分配任意值。
问题(与UglyCoder共享但不与Cameron共享)是因为它使换行符和注释显着...这是意料之外的。因此,有人可以添加带有空格或评论的条目,而无需调整TEST_SIZE
的计算。
答案 2 :(得分:7)
(src) => Splitter.SplitTextByEachDelimiter({";d:",";"})(src){1}
答案 3 :(得分:3)
您可以尝试的一个技巧是在列表末尾添加枚举值并将其用作大小。在你的例子中
enum class Example { A, B, C, D, E, ExampleCount };
答案 4 :(得分:2)
不,你必须在代码中写下它。
答案 5 :(得分:2)
有一个基于X()的技巧 - 宏: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
...
答案 6 :(得分:2)
可以通过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;
}
答案 7 :(得分:2)
还有另一种不依赖行数或模板的方式。唯一的要求是将枚举值粘贴在自己的文件中,并使预处理器/编译器进行计数,如下所示:
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
。
答案 8 :(得分:1)
你也可以考虑消除额外元素的static_cast<int>(Example::E) + 1
。
答案 9 :(得分:1)
对此的另一种“愚蠢”解决方案是:
with
prep1 (tablename, operation, orgname, fragment) as (
select tablename, operation, orgname,
json_object( key 'canonicalName' value orgname,
key 'synonyms'
value nullif(json_arrayagg(synonyms order by synonyms), '[]')
FORMAT JSON ABSENT ON NULL
)
from t
group by tablename, orgname, operation
)
, prep2 (tablename, operation, org_str) as (
select tablename, operation, json_arrayagg(fragment order by orgname)
from prep1
group by tablename, operation
)
select tablename, json_objectagg(key operation value org_str) as json_str
from prep2
group by tablename
;
TABLENAME JSON_STR
--------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ITEM {"add":[{"canonicalName":"Apple Computers","synonyms":["Apple","Apple Inc"]},{"canonicalName":"Google India","synonyms":["Google"]},{"canonicalName":"IBM","synonyms":["IBM Corporation"]}],"update":[{"canonicalName":"Infosys","synonyms":["Infosys Tech"]},{"canonicalName":"Wipro Tech","synonyms":["Wipro Technology"]}],"delete":[{"canonicalName":"IBM","synonyms":["IBM Corporation"]},{"canonicalName":"TCS"}]}
ORG {"add":[{"canonicalName":"Apple Computers","synonyms":["Apple","Apple Inc"]},{"canonicalName":"Google India","synonyms":["Google"]},{"canonicalName":"IBM","synonyms":["IBM Corporation"]}],"update":[{"canonicalName":"Infosys","synonyms":["Infosys Tech"]},{"canonicalName":"Wipro Tech","synonyms":["Wipro Technology"]}],"delete":[{"canonicalName":"IBM","synonyms":["IBM Corporation"]},{"canonicalName":"TCS"}]}
通过使用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;
}();
进行编译,如果您省略或重复了任何切换用例,请确保收到编译器警告。它也是constexpr,因此它是在编译时计算的。
但是请注意,即使对于en -Werror=switch
,即使枚举的第一个值不为0,默认初始化值也为0。因此,您必须从0开始或显式使用第一个值。>
答案 10 :(得分:0)
如果您使用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;
这只是针对Boost预处理器工具的表面。例如,您的宏还可以为强类型枚举定义往返字符串转换实用程序和ostream运算符。
更多有关升压预处理器工具的信息: https://www.boost.org/doc/libs/1_70_0/libs/preprocessor/doc/AppendixA-AnIntroductiontoPreprocessorMetaprogramming.html
顺便说一句,我碰巧非常同意@FantasticMrFox,即如果使用Count
语句,那么在接受的答案中使用的其他switch
枚举值将产生编译器警告,令人头痛。我发现unhandled case
编译器警告对于更安全的代码维护非常有用,因此我不想破坏它。
答案 11 :(得分:0)
对于C ++ 17,您可以使用lib https://github.com/Neargye/magic_enum中的magic_enum :: enum_count magic_enum :: enum_count()-> 4。
答案 12 :(得分:0)
Reflection TS,尤其是latest version of the Reflection TS draft的[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 ++委员会夏季会议上的Trip report: Summer ISO C++ standards meeting (Rapperswil)所述,Reflection TS已被宣布为功能完善
Reflection TS已完成功能:Reflection TS已被宣布为功能已完成,并将在今年夏季进行主要评论投票。再次注意,TS当前基于元编程的模板语法只是一个占位符;所要求的反馈意见是在设计的“核心”上进行的,委员会已经知道它打算用一种简单的编程模型替换表面语法,该模型使用普通的编译时代码而不是
<>
风格的元编程。
并且是initially planed for C++20,但目前尚不清楚Reflection TS是否仍然有机会进入C ++ 20版本。
答案 13 :(得分:0)
这是在 2020 年对我有用的解决方案,使用 Visual Studio 2019
#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