111

多くの場合、いくつかの列挙型を一緒に必要とします。時々、名前の衝突があります。これに対する 2 つの解決策が思い浮かびます。名前空間を使用するか、「より大きな」列挙要素名を使用します。それでも、名前空間ソリューションには 2 つの可能な実装があります。ネストされた列挙型を持つダミー クラス、または本格的な名前空間です。

3つのアプローチすべての長所と短所を探しています。

例:

// oft seen hand-crafted name clash solution
enum eColors { cRed, cColorBlue, cGreen, cYellow, cColorsEnd };
enum eFeelings { cAngry, cFeelingBlue, cHappy, cFeelingsEnd };
void setPenColor( const eColors c ) {
    switch (c) {
        default: assert(false);
        break; case cRed: //...
        break; case cColorBlue: //...
        //...
    }
 }


// (ab)using a class as a namespace
class Colors { enum e { cRed, cBlue, cGreen, cYellow, cEnd }; };
class Feelings { enum e { cAngry, cBlue, cHappy, cEnd }; };
void setPenColor( const Colors::e c ) {
    switch (c) {
        default: assert(false);
        break; case Colors::cRed: //...
        break; case Colors::cBlue: //...
        //...
    }
 }


 // a real namespace?
 namespace Colors { enum e { cRed, cBlue, cGreen, cYellow, cEnd }; };
 namespace Feelings { enum e { cAngry, cBlue, cHappy, cEnd }; };
 void setPenColor( const Colors::e c ) {
    switch (c) {
        default: assert(false);
        break; case Colors::cRed: //...
        break; case Colors::cBlue: //...
        //...
    }
  }
4

8 に答える 8

81

元の C++03 の回答:

(よりも)の利点は、必要なときに宣言を使用できることです。namespaceclassusing

a を使用する際の問題namespaceは、名前空間がコード内の他の場所に展開される可能性があることです。大規模なプロジェクトでは、2 つの異なる列挙型が両方とも呼び出されているとは思わないという保証はありません。eFeelings

より単純に見えるコードの場合struct、コンテンツを公開する必要があると思われるため、 を使用します。

これらのプラクティスのいずれかを行っている場合は、時代を先取りしており、おそらくこれ以上精査する必要はありません.

新しい C++11 のアドバイス:

C++11 以降を使用している場合はenum class、enum の名前内の enum 値を暗黙的にスコープします。

を使用enum classすると、暗黙的な変換と整数型への比較が失われますが、実際には、あいまいなコードやバグのあるコードを発見するのに役立つ場合があります。

于 2009-01-27T17:16:45.380 に答える
21

参考までに、C++0x には、あなたが言及したような場合の新しい構文があります ( C++0x wiki ページを参照してください) 。

enum class eColors { ... };
enum class eFeelings { ... };
于 2009-01-27T21:55:27.267 に答える
12

私は前述の回答を次のようなものにハイブリッド化しました:(編集:これはC ++ 11より前の場合にのみ役立ちます。C++11を使用している場合は、を使用してくださいenum class

すべてのプロジェクト列挙型を含む1つの大きなヘッダーファイルがあります。これらの列挙型はワーカークラス間で共有されており、ワーカークラス自体に列挙型を配置することは意味がないためです。

パブリックを回避しますstruct:シンタックスシュガー、およびtypedef他のワーカークラス内でこれらの列挙型の変数を実際に宣言できるようにします。

名前空間を使用してもまったく役に立たないと思います。おそらくこれは私がC#プログラマーであり、値を参照するときに列挙型の名前を使用する必要あるためです。そのため、私はそれに慣れています。

    struct KeySource {
        typedef enum { 
            None, 
            Efuse, 
            Bbram
        } Type;
    };

    struct Checksum {
        typedef enum {
            None =0,
            MD5 = 1,
            SHA1 = 2,
            SHA2 = 3
        } Type;
    };

    struct Encryption {
        typedef enum {
            Undetermined,
            None,
            AES
        } Type;
    };

    struct File {
        typedef enum {
            Unknown = 0,
            MCS,
            MEM,
            BIN,
            HEX
        } Type;
    };

..。

class Worker {
    File::Type fileType;
    void DoIt() {
       switch(fileType) {
       case File::MCS: ... ;
       case File::MEM: ... ;
       case File::HEX: ... ;
    }
}
于 2013-02-20T19:20:20.003 に答える
10

このためにクラスを使用することは絶対に避けます。代わりに名前空間を使用してください。問題は、名前空間を使用するか、列挙値に一意のIDを使用するかということです。個人的には、名前空間を使用して、IDを短くし、できればもっとわかりやすくします。次に、アプリケーションコードは「usingnamespace」ディレクティブを使用して、すべてを読みやすくすることができます。

上記の例から:

using namespace Colors;

void setPenColor( const e c ) {
    switch (c) {
        default: assert(false);
        break; case cRed: //...
        break; case cBlue: //...
        //...
    }
}
于 2009-01-27T09:39:34.537 に答える
8

クラスを使用する利点は、その上に本格的なクラスを構築できることです。

#include <cassert>

class Color
{
public:
    typedef enum
    {
        Red,
        Blue,
        Green,
        Yellow
    } enum_type;

private:
    enum_type _val;

public:
    Color(enum_type val = Blue)
        : _val(val)
    {
        assert(val <= Yellow);
    }

    operator enum_type() const
    {
        return _val;
    }
};

void SetPenColor(const Color c)
{
    switch (c)
    {
        case Color::Red:
            // ...
            break;
    }
}

上記の例が示すように、クラスを使用すると次のことができます。

  1. (悲しいことに、コンパイル時ではありません) C++ が無効な値からのキャストを許可することを禁止します。
  2. 新しく作成された列挙型に (ゼロ以外の) デフォルトを設定します。
  3. 選択肢の文字列表現を返すなど、さらにメソッドを追加します。

operator enum_type()C++ がクラスを基になる列挙型に変換する方法を認識できるように、宣言する必要があることに注意してください。switchそうしないと、型をステートメントに渡すことができません。

于 2012-04-22T15:41:18.637 に答える
7

クラスまたは名前空間を使用する場合の違いは、名前空間のようにクラスを再度開くことができないことです。これにより、名前空間が将来的に悪用される可能性を回避できますが、列挙のセットにも追加できないという問題もあります。

クラスを使用する利点は、名前空間の場合とは異なり、テンプレート型の引数として使用できることです。

class Colors {
public:
  enum TYPE {
    Red,
    Green,
    Blue
  };
};

template <typename T> void foo (T t) {
  typedef typename T::TYPE EnumType;
  // ...
}

個人的には、私は を使用するのが好きではなく、完全修飾名を好むので、それが名前空間のプラスになるとは思えません。ただし、これはプロジェクトで行う最も重要な決定ではない可能性があります。

于 2009-01-27T10:58:48.043 に答える
5

列挙型はそれを囲むスコープにスコープが設定されているため、グローバル名前空間の汚染を回避し、名前の衝突を回避するために、列挙型を何かでラップするのがおそらく最善です。私がクラスよりも名前空間を好むのは、単純namespaceに保持の袋のように感じられるのに対しclass、堅牢なオブジェクトのように感じられるからです ( structvs.classディベートを参照)。名前空間の考えられる利点は、後で拡張できることです。これは、変更できないサードパーティ コードを扱っている場合に役立ちます。

もちろん、C++0x で列挙型クラスを取得する場合、これはすべて意味がありません。

于 2009-01-27T15:25:37.723 に答える
3

また、列挙型をクラスでラップする傾向があります。

Richard Corden が指摘したように、クラスの利点は、それが c++ の意味での型であり、テンプレートで使用できることです。

基本的な機能を提供するすべてのテンプレートに特化した、私のニーズに合わせた特別なツールボックス::Enum クラスがあります (主に、I/O が読みやすくなるように enum 値を std::string にマッピングします)。

私の小さなテンプレートには、許可された値を実際にチェックするという追加の利点もあります。コンパイラは、値が実際に列挙型に含まれているかどうかをチェックするのが面倒です。

typedef enum { False: 0, True: 2 } boolean;
   // The classic enum you don't want to see around your code ;)

int main(int argc, char* argv[])
{
  boolean x = static_cast<boolean>(1);
  return (x == False || x == True) ? 0 : 1;
} // main

意味のない列挙値が残っているため(そして期待しない)、コンパイラがこれをキャッチしないことが常に気になりました。

同様に:

typedef enum { Zero: 0, One: 1, Two: 2 } example;

int main(int argc, char* argv[])
{
  example y = static_cast<example>(3);
  return (y == Zero || y == One || y == Two) ? 0 : 1;
} // main

もう一度 main がエラーを返します。

問題は、コンパイラが利用可能な最小の表現 (ここでは 2 ビットが必要) に列挙型を適合させ、この表現に適合するものはすべて有効な値と見なされることです。

列挙型に値を追加するたびにすべてのスイッチを変更する必要がないように、スイッチの代わりに可能な値をループしたい場合があるという問題もあります。

全体として、私の小さなヘルパーは列挙型の処理を本当に楽にしてくれます (もちろん、いくらかのオーバーヘッドが追加されます)。

于 2009-09-24T06:47:53.193 に答える