web-dev-qa-db-ja.com

C ++列挙クラスの要素の数を決定することは可能ですか?

C++ enum classのカーディナリティを決定することは可能ですか?

enum class Example { A, B, C, D, E };

sizeofを使用しようとしましたが、enum要素のサイズを返します。

sizeof(Example); // Returns 4 (on my architecture)

カーディナリティを取得する標準的な方法はありますか(私の例では5)。

59
bquenin

直接ではありませんが、次のトリックを使用できます。

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を列挙値の引数として使用できることです。したがって、これを使用する場合は注意してください。 (個人的には、これは実際問題ではないと思います。)

54
Cameron
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つの点で改善されています。

  • Type_safe列挙には余分な要素はありません(BEGINおよびSIZE)( Cameronの答え もこの問題を抱えています。)
    • コンパイラーは、switchステートメントからそれらが欠落していることについて文句を言いません(重大な問題)
    • それらは、あなたのenumを期待する関数に不注意に渡されることはありません。 (一般的な問題ではありません)
  • 使用するためにキャストする必要はありません。 ( キャメロンの答え もこの問題を抱えています。)
  • 減算は、enumクラスタイプのサイズを混乱させません。

glyCoder'sCameron's answer に比べて、列挙子に任意の値を割り当てることができるという利点があります。

問題( glyCoder と共有されているが Cameron とは共有されていない)は、改行とコメントが重要になることです...これは予想外です。したがって、誰かがTEST_SIZEの計算を調整せずに、空白またはコメントを含むエントリを追加できます。

16
Eponymous

C++ 17の場合、libから_magic_enum::enum_count_を使用できます https://github.com/Neargye/magic_enum

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

7
Neargye
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;
7
UglyCoder

試すことができる1つの方法は、リストの最後に列挙値を追加し、それをサイズとして使用することです。あなたの例では

enum class Example { A, B, C, D, E, ExampleCount };
3
David Nehme

いいえ、コードでそれを書く必要があります。

2
user1944441

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
...
2
Kirill Suetnov

余分な要素を削除するstatic_cast<int>(Example::E) + 1も検討できます。

1
Fabio A. Correa

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コンパイラの警告は、より安全なコードメンテナンスに非常に役立つため、これを弱体化させたくありません。

0
arr_sea