3

C++11 では、改良さenumれたenum struct. しかし、これは確かに - あなたがそれに慣れるまで - 下位の古いものの最も驚くべき落とし穴に苦しんでいますenum: 型の変数の値はenum struct E、列挙されたもののいずれかである必要はありませんが、Eの基になる整数型:

enum struct X { one = 1};
X x = X(); // Fine! The value of x is int(0)
x = X(2); // Cool! The value of x is int(2)

enumまたはenum structtypeのオブジェクトを使用するあらゆる場所で、それが「 のE1 つではない」ケースをキャッチする必要がありますE

インスタンス化クラスのオブジェクトが「列挙された」もの以外の値を想定できないというプロパティを使用して、(必ずしもドロップイン代替であるとは限らない)代わりに使用できるジェネリック型を定義するにはどうすればよいでしょうか?enum struct

つまり、オブジェクトが区別されない値を想定するのではなく、スローする場合に満足することはできません(つまり、「s の1 つにならない」場合をキャッチしますE)。

そして、これらの値が一連の整数型テンプレート パラメーターによって "列挙" され、X::one.

それが避けられない場合は、「列挙された」値が型から列挙インデックスによって静的に取得可能な定数になる限り、問題ありません。(そうすれば、クライアント コードで、意味のあるシンボルをインデックスまたはインデックス付きの値に関連付けて、そのような便利なマッピングをカプセル化することが簡単になります。たとえば、structネストされた匿名でenum!)

私が知らないこの質問に対する定評のある解決策は既にありますか?

コメンテーターのリクエストにより続きます (Ali)

疑似コードを投稿できますか?どのように使用したいかを示す必要があります。

ここに、望ましい使用法のいくつかの兆候があります(私は思います):

/* 1 */
/*  These 2 structs could just be tag-types X{}, Y{}
    serving to distinguish value-safe-enums that
    would otherwise be the same. But you could
    also get more mileage out them, as shown...
*/
struct turn;
struct hand;

using vs_turn = val_safe_enum<turn,int,1,2>;
using vs_hand = val_safe_enum<hand,int,1,2>;

struct turn
{
    // Want an anonymous scoped enum; only rvalue 
    enum {
        right = vs_turn::which<0>(), // = 1
        left = vs_turn::which<1>() // = 2
    };
};

struct hand
{
    enum {
        right = vs_hand::which<0>(), //= 1
        left = vs_hand::which<1>() // = 2
    };
};
/* End 1 */

/* 2 */
char const * foo(vs_turn const & t) {
    // Only 2 possibilities!
    return int(t) == turn::right ? "right turn" : "left turn";
}
char const * foo(vs_hand const & h) {
    return int(h) == hand::right ? "right hand" : "left hand";
} 
/* End 2 */  


vs_turn t1(0); // 3.a OK
vs_turn t2(turn::right); // 3b. OK
vs_hand h1(hand::left); // 3c. OK
vs_hand h2(1); // 3d. OK

t1 == vs_turn(turn::right); // 4. OK

t1 < t2; // 5. OK

t1 = t2; // 6. OK

int(t1) == turn::right; // 7. OK. Explicit conversion to underlying type.

/* 8 */ 
switch(int(t1)) {
case turn::right:
    /* Something */ break;
case turn::left:
    /* Something */;
    // No default necessary! 
}
/* End 8 */

vs_turn t3(3); // 9. Throw! Value out of range
vs_turn t4; // 10. Error. No default construction.

t1 == turn::right; // 11a. Error. No Conversion
t1 <= h1; // 11b. Error. No conversion.
t1 = turn::right; // 11c. Error. No conversion
t1 = h1; // 11d. Error. No conversion.
foo(turn::right); // 11e. Error. No conversion
4

7 に答える 7

0

醜いですが、コンパイル時に失敗します:

enum E { a, b, c };

constexpr E range_check(E e) {
   return e <= c ? e : (throw "enum value out of range"); // fails at compile time, see below
}

class wrapper {
  public:
    constexpr wrapper(E e) : e(range_check(e)) { }
  private:
    enum E e;
};

int main() {
  constexpr wrapper w((E)42);
}

エラーメッセージが役に立たないため、このアプローチはあまり好きではありません。それにもかかわらず、コンパイル時に失敗することが保証されています。

これはあなたが探しているものですか?

于 2013-06-16T14:01:50.553 に答える
0

C++11 のイディオムに C++03 の問題がありました。

引数が 1 つの「コンストラクター呼び出し」表記X(2)は、実際には C スタイルの cast の省略形であり(X) 2、これは toxic に変換されreinterpret_cast< X >( 2 )ます。スコープ列挙型のセールス ポイントの 1 つは、 との間で変換されないことですint

Type( initializer )クラス型以外での構文の使用は避けてください。予期しない値が得られることはありません。(うーん、GCC にこれに対する警告があればいいのですが、見つかりません。)

コード内で「均一な初期化」X{ 2 }に置き換えると、コンパイラは適切なエラーを表示します。

于 2013-06-17T10:21:47.473 に答える
0

これはどうですか?

struct MyColor {
public:
    static MyColor Red; // defined as MyColor(red)
    static MyColor Blue; // defined as MyColor(blue)

    // Copy constructors, assignment etc. goes here
private:
    MyColor(Value value);
    enum Value { red, blue, green };
    Value value;
};
于 2013-06-16T09:47:17.117 に答える