2011-01-18 13 views
1

私はこの「より良い」列挙型クラスを持っているC++での型変換をどのようにしてenum型にコンパイルできないのですか?

  1. 無効な値を含めることはできません、と
  2. 、列挙型の値が明示的に設定されていないまで使用

次のようにすることはできません。

class Symmetry 
{ 
    public: 
     enum Type { 
      GENERAL, SYMMETRIC, HERMITIAN, 
      SKEW_SYMMETRIC, SKEW_HERMITIAN, UNINITIALIZED 
     }; 

     Symmetry() { t_ = UNINITIALIZED } 
     explicit Symmetry(Type t) : t_(t) { checkArg(t); } 
     Symmetry& operator=(Type t) { checkArg(t); t_ = t; return *this; } 

     operator Type() const { 
      if (t_ == UNINITIALIZED) throw runtime_error("error"); 
      return t_; 
     } 

    private: 
     Type t_; 

     void checkArg(Type t) { 
      if ((unsigned)t >= (unsigned)UNINITIALIZED) 
       throw runtime_error("error"); 
     } 
}; 

これにより、次のコードを書くことができます。

Symmetry s1(Symmetry::SYMMETRIC); 
Symmetry s2; 
s2 = Symmetry::HERMITIAN; 
Symmetry s3; 
if (Symmetry::GENERAL == s3) // throws 

私の問題は、コンパイラのような構造が可能になることである:

Symmetry s1((Symmetry::Type)18); // throws 
Symmetry s2; 
s2 = (Symmetry::Type)18; // throws 

が、私は例外をスローすることによってこの問題を解決しますが、私は全く(コンパイル時のエラー)をコンパイルしていないようなコードを好むだろう。これを管理する方法はありますか?

+0

あなたはコンパイル時定数のために問題を解決できたとしても、どのような誰かがしようとした場合、 'シンメトリーS1((対称::タイプ)は、x);'? –

+0

intからのすべてのキャストを禁止するか、無効なintからのキャストを禁止するだけですか? –

+0

Oliに:それはポイントです、私はこのコードをコンパイルしないことを望みます。どういうわけか、他のタイプのSymmetry :: Type型にキャストできないようにしてください。 –

答えて

4

潜在的に厄介な解決策ですが、それはあなたの直面する問題を解決します。内部の列挙型を持つよりも、プライベートコンストラクタで小さなヘルパークラスを定義し、外部クラスをフレンドにする。次に、 "列挙型"の値は、外部クラスのstatic constメンバーになります。このような何か:

免責事項:テストされていないので、そこに様々なコンパイルの問題かもしれないが、あなたのアイデアを取得する必要があります)

class Symmetry 
{ 
public: 
    class Type 
    { 
    private: 
     Type() {}; 
     friend class Symmetry; 
    }; 

    static const Type GENERAL; 
    static const Type SYMMETRIC; 
    static const Type HERMITIAN; 
}; 

あなたは平等を決定するいくつかの方法が必要になりますが、これはあるべきかなり簡単。

+0

これ。必要ならば、さらに一歩進んだり、 'Type'基本クラスを持ち、静的constメンバーダミーオブジェクトを持つ' GENERAL'、 'SYMMETRIC'などのサブクラスを派生させることもできます。コンパイル時に列挙型の 'values'に関数のオーバーロードができるようになります:) – Mephane

+0

しかし、実行時に基づいた動作のために仮想関数と参照セマンティクスが必要になります。 – Puppy

+2

私が考えることができる1つの問題は、アプリケーションの初期化またはシャットダウン時にこれらの列挙型を安全に使用できないことです(これはすべてのグローバルで当てはまります)。ローカル静的を使用すると、関数呼び出しを必要とするコストでこの懸念が緩和されます。 –

0

いいえ、キャストの使用を許可した場合、最後の例のように、キャストは常に使用され、そのタイプを覆すことができます。

解決策は、これらのキャストを使用する習慣ではなく、これらのキャストを無差別に使用するコードを非常に疑わしく考えることです。あなたの兵器の核兵器としてこのタイプのキャスティングを見てください:それは重要ですが、あなたは常に注意してそれを扱い、決してそれをまれに配備することは決してありません。

コンパイラはキャストにどのような警告オプションを備えていますか?このようなキャストの誤用を検出する可能性のある糸くずツールは何ですか?


つまり、実際には内部のタイプを隠してユーザーが使用したくないように思われます。それは少しあなたの元を微調整することで、すべてのキャスト誤用を防止していないながらも、straight-forward to make that type name privateだ、ということを実現:

struct Symmetry { 
    enum { 
    UNINITIALIZED, 
    GENERAL, SYMMETRIC, HERMITIAN, 
    SKEW_SYMMETRIC, SKEW_HERMITIAN 
    }; 

private: 
    typedef decltype(UNINITIALIZED) Hidden; 
    Hidden _value; 

public: 
    Symmetry(Hidden value = UNINITIALIZED) : _value (value) {} 
    Symmetry& operator=(Hidden value) { _value = value; return *this; } 
    operator Hidden() const { 
    if (_value == UNINITIALIZED) { 
     throw std::logic_error("uninitialized Symmetry"); 
    } 
    return _value; 
    } 

    bool initialized() const { return _value != UNINITIALIZED; } 
    // required if you want to check for UNINITIALIZED without throwing in 
    // the above conversion 
}; 

これは、初期化の順序で完全に実装し、何の詳細は省略していないか、未知の、または問題です。唯一の注意点はdecltypeです。プリC++ 0xコンパイラでは、実装固有のものを使用するか、実装固有のものをラップするlibraryを使用する必要があります。

さらに小さな問題:runtime_errorからlogic_errorに変更してください。初期化されていない値を使用すると事前に予防でき、後者のカテゴリに入るはずです。

+0

残念ながら、decltypeもBOOST_TYPEOFも(anonymous enumを使って)動作しません。コンパイラエラーが発生し、GNUとインテルを試しました。 –

+0

@Daniel:完全な0xサポートまで、最も簡単な回避策は、列挙型に名前を付けることです。 DoNotUseThisのように、名前は明らかに非公開にしてください。もちろん、人々はそれを使い続けることができますが、あなたはそれらを止めることはできませんが、現実は上記のコードでもありません。これが私が最初の部分でやったように答えた理由です。 –

1

テンプレートを使った私の試み:(テスト済みですが、これはさらに改善することができます!)

template<int N> 
struct Symmetry 
{ 
    enum Type 
    { 
     GENERAL, SYMMETRIC, HERMITIAN, 
     SKEW_SYMMETRIC, SKEW_HERMITIAN 
    }; 
    template<Type e> struct allowed; 
    template<> struct allowed<GENERAL>  { static const int value = GENERAL; }; 
    template<> struct allowed<SYMMETRIC>  { static const int value = SYMMETRIC; }; 
    template<> struct allowed<HERMITIAN>  { static const int value = HERMITIAN; }; 
    template<> struct allowed<SKEW_SYMMETRIC> { static const int value = SKEW_SYMMETRIC; }; 
    template<> struct allowed<SKEW_HERMITIAN> { static const int value = SKEW_HERMITIAN; }; 

    allowed<(Type)N> m_allowed; 

    operator int() 
    { 
     return N; 
    } 
}; 

Symmetry<0> e0;     //okay 
Symmetry<1> e1;     //okay 
Symmetry<100> e4;     //compilation error! 
Symmetry<e0.SKEW_HERMITIAN> e3; //okay 
Symmetry<e0.SKEW_SYMMETRIC> e3; //okay 

使用法:

int main() 
{ 
    Symmetry<0> e0;     
    Symmetry<e0.HERMITIAN> e1;   

    switch (e1) 
    { 
    case e0.HERMITIAN: 
     { 
      cout << "It's working" << endl; 
     } 
     break; 
    } 
} 
+0

これで、すべての対称 "変数"は、コンパイル時に固定された値を持たなければなりません! (不変よりもさらに厳しいです。)それが望ましい場合、enumなどの定数を使用するだけで、すでにあなたが提供されています。 –

+0

@Fred:enumでは、他の値も割り当てることができます。上記の方法を使用すると、「明示的に」許可されている以外の値を使用することはできません。 – Nawaz

+0

@ Nawaz:上記のコードを使用して、ユーザーの入力を受け入れ、有効なSymmetry値をとる関数に与えることができますか? (No.)変化しない変数のポイントは何ですか?すべてのコードは、HERMITIAN(私が言及した列挙定数)の名前を別のものに変更します。 –

関連する問題