連続列挙型C ++ 11


17

列挙型が連続しているかどうかをC ++ 11でチェックする方法はありますか?

ない列挙値を与えることは完全に有効です。C ++ 14、C ++ 17、またはC ++ 20の型特性のような機能があるのでしょうか?これはstatic_assertで使用されます。

次に小さな例を示します。

enum class Types_Discontinuous {
  A = 10,
  B = 1,
  C = 100
};

enum class Types_Continuous {
  A = 0,
  B = 1,
  C = 2
};

static_assert(SOME_TEST<Types_Discontinuous>::value, "Enum should be continuous"); // Fails
static_assert(SOME_TEST<Types_Continuous>::value, "Enum should be continuous");    // Passes

1
昇順であるという意味は続きますか、それともゼロから始まり、すべての値で+1になるという意味ですか
RoQuOTriX

5
列挙ラベルを列挙する方法はないので、プログラム自体からそれを行うことはできません。
プログラマー

1
面白い。コンパイラに階乗を計算させる方法について、テンプレートプログラミングの行に沿って考えています。AとCの2つの境界で開始します。テンプレート関数は、SFINAEを介して、内のそれらの間のすべての値の存在の有無をチェックしますenum。悲しいことに、私は1日の仕事をしているので、これを書き出そうとすることはできませんが、このアプローチに基づいて回答を賛成します。@barryや@seheのような誰かができると確信しています。
バトシェバ

1
@RoQuOTriX値をラベルにどのように一致させますか?また、ラベルの順序をどのように確認しますか?そして、それはコンパイル時にどのように実行できますか(これはに必要ですstatic_assert)?「美しい解決策」を作成できない場合でも、一般的な方法でそれがどのように行われるのか非常に興味があるので、とにかく答えを書いてください。
プログラマー

1
@Someprogrammerdudeあなたが説明したのは「美しい」または良い解決策です。私が意味したのは、すべての列挙型および神の祝福に対して書き換える必要がある「簡単な」チェックソリューションでした。誰もそれを実行しないことを望みます
RoQuOTriX

回答:


7

いくつかのenums については、おそらくMagic Enumライブラリーを使用してこれをハックすることができます。例えば:

#include "magic_enum.hpp"

template <typename Enum>
constexpr bool is_continuous(Enum = Enum{}) {
    // make sure we're actually testing an enum
    if constexpr (!std::is_enum_v<Enum>)
        return false;
    else {
        // get a sorted list of values in the enum
        const auto values = magic_enum::enum_values<Enum>();
        if (std::size(values) == 0)
            return true;

        // for every value, either it's the same as the last one or it's one larger
        auto prev = values[0];
        for (auto x : values) {
            auto next = static_cast<Enum>(magic_enum::enum_integer(prev) + 1);
            if (x != prev && x != next)
                return false;
            else
                prev = x;
        }
        return true;
    }
}

これは実際に、ライブラリ名が示すように「マジック」であることに注意してください。ライブラリは、コンパイラ固有のいくつかのハックで機能します。そのため、「純粋なC ++」の要件を実際に満たすことはできませんが、言語にリフレクション機能ができるようになるまでは、可能な限り優れています。


それは確かに魔法ですが、これは私の状況に最適です。
バート

7

これは純粋なC ++では不可能です。列挙値を列挙したり、値の数や最小値と最大値を調べたりする方法がないためです。しかし、コンパイラの助けを借りて、あなたが望むものに近いものを実装することもできます。たとえば、gccでは、switchステートメントが列挙型のすべての値を処理しない場合、コンパイルエラーが発生する可能性があります。

enum class my_enum {
    A = 0,
    B = 1,
    C = 2
};

#pragma GCC diagnostic push
#if __GNUC__ < 5
#pragma GCC diagnostic error "-Wswitch"
#else
#pragma GCC diagnostic error "-Wswitch-enum"
#endif

constexpr bool is_my_enum_continuous(my_enum t = my_enum())
{
    // Check that we know all enum values. Effectively works as a static assert.
    switch (t)
    {
    // Intentionally no default case.
    // The compiler will give an error if not all enum values are listed below.
    case my_enum::A:
    case my_enum::B:
    case my_enum::C:
        break;
    }

    // Check that the enum is continuous
    auto [min, max] = std::minmax({my_enum::A, my_enum::B, my_enum::C});
    return static_cast< int >(min) == 0 && static_cast< int >(max) == 2;
}

#pragma GCC diagnostic pop

明らかに、これは特定の列挙型に特化していますが、そのような関数の定義はプリプロセッサで自動化できます。


私が正しく理解している場合でも、スイッチのすべての列挙値とminmaxのリストを書き込む必要があります。現在、私は複数の列挙型を持っているので、それは確かに可能ですが、私の状況では好ましくありません。
バート

1

私はこれについての答えを見たいです。私もそれを必要としてきました。

残念ながら、既存のユーティリティを使用してこれを行うことはできないと思います。これに型の特性を実装したい場合は、コンパイラーのサポートが必要なので、そのためのテンプレートを作成するのは現実的ではありません。

列挙型を特定のタグで拡張して、それが隣接していてすぐにサイズがわかることを示しています:enumクラスコンストラクターc ++、特定の値を渡す方法は?

または、独自の特性を記述することもできます。

 template<T> struct IsContiguous : std::false_type {};

これを使用する連続した列挙型を定義する場合は常に、これを特殊化する必要があります。残念ながら、列挙型が変更された場合、これにはある程度のメンテナンスと注意が必要です。


1
タイプが正しい設定されている場合は、コンパイル時にチェックコードチェッカーを書くことができる
RoQuOTriX

はい、そうです。あなたがそれを書く能力を持っているなら。
JVApen

1

すべての列挙型は連続しています。0は常に許可されます。許可される最大値は、次の値1<<N -1(すべてのビットが1)に切り上げられた最高の列挙子であり、その間のすべての値も許可されます。([dcl.enum] 9.7.1 / 5)。負の列挙子が定義されている場合、許可される最小値は、最小の列挙子を切り捨てることによって同様に定義されます。

で定義されてenumいる列挙子は、範囲内の値と正しい型を持つ定数式ですがenum、同じプロパティを持つの外に追加の定数を定義できます。

constexpr enum class Types_Discontinuous = static_cast<Types_Discontinuous>(2)


2
あなたは正しいですが、定義された値についてこれを知りたいことはOPから明らかです。(
追記

1
@JVApen:それがまさに問題です。「定義された値」は列挙型自体のプロパティではありません。標準は、列挙型の値が何であるかを明示しています。
MSalters
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.