10

私のプロジェクトでは、これに似た enum 宣言がいくつかあります。

enum Comparison
{
    LT,     // "<"
    GT,     // ">"
    EQ,     // "=="
    LTEQ,   // "<="
    GTEQ,   // ">="
    NEQ     // "!="
};
enum Arithmetic
{
    ADD,    // "+"
    SUB,    // "-"
    MUL,    // "*"
    DIV,    // "/"
    MOD,    // "%"
};

そして、これらのいくつかを組み合わせて、1 つの組み合わせた列挙型にしたいと思います。

  • (サブ列挙からの) すべての要素は、結合された列挙に存在します。
  • すべての要素には一意の値があります (当然のことです)。
  • すべての要素は、結合された列挙型と元の列挙型で一貫した値を持ちます。

このような:

enum Comparison
{
    LT,     // "<"
    GT,     // ">"
    EQ,     // "=="
    LTEQ,   // "<="
    GTEQ,   // ">="
    NEQ     // "!="

    ADD,    // "+"
    SUB,    // "-"
    MUL,    // "*"
    DIV,    // "/"
    MOD,    // "%"
};

また、私ができるようにしたいのは、結合された列挙型のみの値を指定して、結合された列挙型を元の列挙型の 1 つに「キャスト」することです (値が一貫していると仮定すると、簡単なはずです)。

operator int()列挙型の代替は、クラスが演算子を実装するクラスベースのソリューションです。

ノート; 私はそれoperator int()がどういうわけか進むべき道だと信じています。

4

8 に答える 8

20

私が一般的に見たのはこれです:

enum OperationType {
    Comparison = 0x100,
    Arithmetic = 0x200
};        

enum ComparisonType
{
    LT = Comparison,     // "<"
    GT,     // ">"
    EQ,     // "=="
    LTEQ,   // "<="
    GTEQ,   // ">="
    NEQ     // "!="
};
enum ArithmeticType
{
    ADD = Arithmetic,    // "+"
    SUB,    // "-"
    MUL,    // "*"
    DIV,    // "/"
    MOD,    // "%"
};

これにより、単純な連鎖よりも少し柔軟性が増します。なぜなら、算術を中断することなく比較を追加できるようになり、算術と比較が互いについて知る必要がないからです。列挙型の型を取得することも簡単になります。

constexpr OperationType getOperationType(unsigned value)
{return static_cast<OperationType>(value&0xFF00);}
于 2013-08-20T21:04:18.383 に答える
6

連鎖する一般的な (しかし例外的にエレガントではない) 方法enum(たとえば、子クラスが一意のセットを拡張する必要がある場合) は、それぞれenumが「最後の」値を提供し、それを使用して次の値を開始することです。

enum Comparison
{
    LT,     // "<"
    ...
    NEQ,    // "!="
    LastComparison
};

enum Logical
{
    AND = LastComparison,
    OR,
    ...
    LastLogical
};
于 2013-08-20T20:50:13.550 に答える
0

列挙型の「キャスト」について、私は列挙型の差別化された結合について考えていました(Boost Variant のようなものですが、(暗黙的な) 変換と、特に列挙型に合わせたその他の便利さを備えています。手間をかけずに:

2 つの列挙型があるとします。

enum A { A_1, A_2, A_3, A_4 };
enum B { B_1, B_2, B_3, B_4 };

識別された共用体を提案するので、列挙型メンバーの一意性については気にしないことに注意してください。AorBここで、次のように動作する型を使用できるようにしたいと考えています。

A a = A_3;
B b = B_1;

AorB any;

// any is isNil now
any = b; // makes it isB
any = a; // makes it isA

if (any == A_2) // comparison is fine, because any is in `isA` now
{
    std::cout << "Whoops, should be A_3, really\n"; // doesn't happen
}

if (any == B_2) // comparison
{
    std::cout << "Whoops, should not match"; // doesn't happen
}

a = static_cast<A>(any); // valid cast
b = static_cast<B>(any); // fails assertion

これが私の見解です:

#include <cassert> // for assert
#include <utility> // for std::swap

struct AorB
{
    enum Discriminant { isNil, isA, isB } discriminant;

    union
    {
        A valA;
        B valB;
    };

    AorB() : discriminant(isNil) {}

    A asA() const { assert(discriminant==isA); return valA; }
    B asB() const { assert(discriminant==isB); return valB; }

    explicit operator A() const { return asA(); }
    explicit operator B() const { return asB(); }

    /*explicit*/ AorB(A valA) : discriminant(isA), valA(valA) {}
    /*explicit*/ AorB(B valB) : discriminant(isB), valB(valB) {}

    friend void swap(AorB& a, AorB& b) {
        auto tmp = a; 
        a.discriminant = b.discriminant;
        a.safe_set(b.safe_get());

        b.discriminant = tmp.discriminant;
        b.safe_set(tmp.safe_get());
    }

    AorB& operator=(AorB implicit_conversion) {
        swap(implicit_conversion, *this);
        return *this;
    }

    bool operator==(AorB other) const {
        return 
            discriminant == other.discriminant && 
            safe_get()   == other.safe_get();
    }

  private:
    void safe_set(int val) {
        switch(discriminant) {
            case isA: valA = static_cast<A>(val); break;
            case isB: valB = static_cast<B>(val); break;
            case isNil: break;
        }
    }
    int safe_get() const {
        switch(discriminant) {
            case isA: return valA;
            case isB: return valB;
            case isNil: 
            default:  return 0;
        }
    }
};

Coliruでライブで見る, 印刷:

main.cpp:20: B AorB::asB() const: Assertion `discriminant==isB' failed.
于 2013-08-20T21:44:50.873 に答える
0

だから私は最近プリプロセッサで似たようなことをしました.この答えは約2年遅れていますが、それでも.

基本的に、相互に拡張できるようにしたい、連続して定義されていないさまざまな列挙型があるため、次のプリプロセッサ ディレクティブを記述しました。

#define START_ENUM(name,extends)\
namespace name##_ns {\
enum name\
{ BASE = extends::LAST  + 1
#define DEF_ENUM(name) , name
#define END_ENUM(name) \
,LAST\
};};\
using namespace name##_ns;

列挙型は、LAST と BASE の複数の定義を避けるために、独自の名前空間で作成されます。名前空間の汚染が気に入らない場合は、それらを列挙型クラスに切り替えることができますが、後で unsigned int にキャストするのが面倒になります。

連続する列挙型を拡張するには、基本列挙子を定義する必要がありますが、スタイルの好みによっては、空にすることもできます

enum class base_action {BASE = 0, LAST = 0}

後続の列挙型はディレクティブで宣言できます

START_ENUM(comparison, base_enum)
DEF_ENUM(LT)
DEF_ENUM(GT)
...
END_ENUM(comparison)

START_ENUM(arithmetic, comparison)
DEF_ENUM(ADD)
...
END_ENUM(arithmetic)

チェーンされた列挙型を作成するのは、単なる構文糖衣です。

これらの列挙型をすべて組み合わせるには、おそらくいくつかのキャストを行う必要があります。単純な構造体を使用して列挙型を統一します

struct EnumValue
{
    EnumValue(unsigned int _val):myVal(_val){}

    //template method allows casting back to original enums and such
    template<typename T>
    T asBaseEnum()
    {
        //optional range checking
        return static_cast<T>(myVal);
    }

    //you could template these too if you want, or add 
    //a templated conversion operator instead 
    //(template<typename T> operator T()) 
    //but I personally don't bother
    operator=(unsigned int _val){myVal = _val}
    operator==(unsigned int _val){myVal == _val}

}
于 2015-11-30T02:09:31.797 に答える
-2

「組み合わせた列挙型をキャストする」という意味がよくわかりませんが、列挙型の組み合わせを可能にするために、ビットフィールドを使用します。

enum Comparison
{
    LT = 0x0001,     // "<"
    GT = 0x0002,     // ">"
    EQ = 0x0004,     // "=="
    LTEQ = 0x0005,   // "<=" - combines LT and EQ
    GTEQ = 0x0006,   // ">=" - combines GT and EQ
    NEQ = 0x0008     // "!="
};

それらのいくつかは一緒にマージできないため (たとえば、何かを LT と GT の両方にすることはできません)、ビットフィールドを調整してそれを防ぐことができます。

編集:

少し違うものを探しているように見えるので:

列挙型をマージする場合は、既に説明した Ben Jackson の方法を使用できます。別の方法は、次のようにすることです。

enum Comparison
{
    LT,
    ...
    NEG
};

enum Logical
{
    AND,
    OR,
    ...
};

enum MyNewCombination
{
    LessThan = LT,
    ...
    NotEqual = NEG,
    And = AND,
    Or = OR,
    ...
};

これにより、既存のすべての列挙型が新しいMyNewCombination列挙型に効果的に移動します。MyNewCombination有効な範囲については、列挙型をComparisonまたは列挙型にキャストできますLogical

于 2013-08-20T20:50:38.137 に答える