C++ enum class
のカーディナリティを決定することは可能ですか?
enum class Example { A, B, C, D, E };
sizeof
を使用しようとしましたが、enum要素のサイズを返します。
sizeof(Example); // Returns 4 (on my architecture)
カーディナリティを取得する標準的な方法はありますか(私の例では5)。
直接ではありませんが、次のトリックを使用できます。
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 };
1つの欠点は、コンパイラがExample::Count
を列挙値の引数として使用できることです。したがって、これを使用する場合は注意してください。 (個人的には、これは実際問題ではないと思います。)
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;
これは glyCoderの答え から派生していますが、3つの点で改善されています。
BEGIN
およびSIZE
)( Cameronの答え もこの問題を抱えています。)glyCoder'sCameron's answer に比べて、列挙子に任意の値を割り当てることができるという利点があります。
問題( glyCoder と共有されているが Cameron とは共有されていない)は、改行とコメントが重要になることです...これは予想外です。したがって、誰かがTEST_SIZE
の計算を調整せずに、空白またはコメントを含むエントリを追加できます。
C++ 17の場合、libから_magic_enum::enum_count
_を使用できます https://github.com/Neargye/magic_enum :
magic_enum::enum_count<Example>()
-> 4。
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;
試すことができる1つの方法は、リストの最後に列挙値を追加し、それをサイズとして使用することです。あなたの例では
enum class Example { A, B, C, D, E, ExampleCount };
いいえ、コードでそれを書く必要があります。
X()マクロに基づく1つのトリックがあります: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
...
余分な要素を削除するstatic_cast<int>(Example::E) + 1
も検討できます。
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
コンパイラの警告は、より安全なコードメンテナンスに非常に役立つため、これを弱体化させたくありません。