2009-03-13 18 views
62

C++の別の列挙型からenumを継承できるパターンはありますか?そのようなベースのenumクラスの継承

何か:

enum eBase 
{ 
    one=1, two, three 
}; 


enum eDerived: public Base 
{ 
    four=4, five, six 
}; 

答えて

42

不可。列挙型の継承はありません。

代わりに、名前付きのconst intでクラスを使用できます。

例:

class Colors 
{ 
public: 
    static const int RED = 1; 
    static const int GREEN = 2; 
}; 

class RGB : public Colors 
{ 
    static const int BLUE = 10; 
}; 


class FourColors : public Colors 
{ 
public: 
    static const int ORANGE = 100; 
    static const int PURPLE = 101; 
}; 
+0

このソリューションに問題はありますか? たとえば、(多態性を深く理解していない)ベクトルを使用できますか?p = std :: find(mycolors、mycolor + length、Colors :: ORANGE); – jespestana

+1

@ jespestanaいいえ、あなたは 'Colors'クラスインスタンスを使用しません。静的constメンバーではint値のみを使用します。 – jiandingzhe

+0

もし私があなたを正しく理解すれば;その後、私はベクトルコンテナを使用する必要があります。しかし、私はまだ書くことができる:p = std :: find(mycolors、mycolor + length、Colors :: ORANGE);.右? – jespestana

75
#include <iostream> 
#include <ostream> 

class Enum 
{ 
public: 
    enum 
    { 
     One = 1, 
     Two, 
     Last 
    }; 
}; 

class EnumDeriv : public Enum 
{ 
public: 
    enum 
    { 
     Three = Enum::Last, 
     Four, 
     Five 
    }; 
}; 

int main() 
{ 
    std::cout << EnumDeriv::One << std::endl; 
    std::cout << EnumDeriv::Four << std::endl; 
    return 0; 
} 
+1

私は混乱しています!どのように変数や関数の引数でEnum型を参照すればいいのですか?EnumDerivが与えられていない関数にEnumDerivが与えられないようにするにはどうすればいいですか? –

+12

これは動作しません。いくつかの関数を定義すると、 'int basic(EnumBase b){return b; } 'と' int由来(EnumDeriv d){戻り値d; } 'の場合、これらの型は' int'に変換できませんが、普通の列挙型です。 'EnumBase :: 'から' EnumBase :: 'への変換は、 'cout << basic(EnumBase :: One)<< endl;'のような単純なコードでさえしようとするとエラーになります。非スカラー型の 'EnumBase'が要求されました。これらの問題は、おそらくいくつかの変換演算子を追加することによって克服することができます。 – SasQ

0

インポッシブル。
しかし、クラス内でenumを匿名で定義し、派生クラスにenum定数を追加することができます。

7

これは直接行うことはできませんが、thisの記事から解決策を試すことができます。

主な考え方は、enum値を保持し、型キャスト演算子を持つヘルパーテンプレートクラスを使用することです。列挙型の基になる型がintであることを考慮すると、列挙型ではなくコードでこのホルダークラスをシームレスに使用できます。

+0

このコードスニペットは問題を解決するかもしれませんが、[説明を含む](http://meta.stackexchange.com/questions/114762/explaining-entirely-code-based-answers)は、あなたの質を改善するのに本当に役立ちます役職。将来読者の質問に答えていることを覚えておいてください。そうした人々はあなたのコード提案の理由を知らないかもしれません。 – NathanOliver

+0

これはすばらしい答えです。それは "問題を別の方法で考える"という事例の1つで、テンプレートを使用するという考え方は本当に法案に適合しています。 – JasonDiplomat

+0

また、このテンプレートの解決策のいくつかを見てみましょう: https://stackoverflow.com/questions/5871722/how-to-achieve-virtual-template-function-in-c – JasonDiplomat

1

派生クラスに同じ名前のenumを定義し、基底クラスの対応先enumの最後のアイテムから開始すると、ほとんどあなたが欲しいものが得られます - 継承した列挙型。このコードでは ルック:

class Base 
{ 
public: 
enum ErrorType 
    { 
    GeneralError, 
    NoMemory, 
    FileNotFound, 
    LastItem 
    } 
} 

class Inherited: public Base 
{ 
enum ErrorType 
    { 
    SocketError = Base::LastItem, 
    NotEnoughBandwidth, 
    } 
} 
+0

コードはコンパイル可能ですが、コンパイラはbase :: ErrorTypeからInherited :: ErrorTypeに変換できないため、このコードを使用することはできません。 – bavaza

+0

@bavaza、確かに、それらの値をパラメータとして渡すときは、enumの代わりにintegerを使うべきです。 – Haspemulator

2

baydaで述べたように、ではない(および/またはすべきでない)を行うのをENUMは機能を持っているので、私はMykola Golubyevの応答を適合させることによって、あなたの苦境に次のアプローチを取ってきました:

typedef struct 
{ 
    enum 
    { 
     ONE = 1, 
     TWO, 
     LAST 
    }; 
}BaseEnum; 

typedef struct : public BaseEnum 
{ 
    enum 
    { 
     THREE = BaseEnum::LAST, 
     FOUR, 
     FIVE 
    }; 
}DerivedEnum; 
+2

このソリューションにはほとんど問題がありません。まず、DerivedEnumの開始点を設定する以外に実際には存在しないLASTでBaseEnumを汚染しています。次に、DerivedEnum値と衝突するBaseEnumの値を明示的に設定したい場合はどうすればよいでしょうか?とにかく、これはおそらくC++ 14のようにできることです。 –

+0

私が述べたように、それは前の例のものですから、完全性のために表現されていますので、前のポスターが論理的な問題を抱えていることは私の心配ではありません – vigilance

4

残念ながら、C++では不可能です。私は、C++でこのような言語機能を使うことを願っています。あなたの問題に対するいくつかの回避策が既にあるので、私は解決策を提供しません。

私は、文言は「継承」ではなく「拡張」でなければならないことを指摘したいと思います。この拡張ではより多くの値が得られます(例では3から6にジャンプします)。継承とは、与えられた基底クラスにより多くの制約を入れて、可能性のセットが小さくなることを意味します。したがって、潜在的なキャスティングは継承とは正反対に動作します。派生クラスを基底クラスにキャストすることはできますが、クラス継承を使用することはできません。しかし、拡張機能を使用する場合は、基底クラスをその拡張機能にキャストすることができなければなりません。私は「そうすべきだ」と言っているのは、私が言ったように、そのような言語機能はまだ存在しないからです。

+0

'extend'は継承のキーワードエッフェル塔。 –

+0

この場合、Liskov Substitution Principleは尊重されないので、あなたは正しいです。 comiteeはこれのために構文的に継承のように見える解決策を受け入れません。 –

3

これはいかがですか?インスタンスはすべての可能な値に対して作成されますが、それ以外にも非常に柔軟です。不利な点はありますか?

.H:

class BaseEnum 
{ 
public: 
    static const BaseEnum ONE; 
    static const BaseEnum TWO; 

    bool operator==(const BaseEnum& other); 

protected: 
    BaseEnum() : i(maxI++) {} 
    const int i; 
    static int maxI; 
}; 

class DerivedEnum : public BaseEnum 
{ 
public: 
    static const DerivedEnum THREE; 
}; 

。CPP:

int BaseEnum::maxI = 0; 

bool BaseEnum::operator==(const BaseEnum& other) { 
    return i == other.i; 
} 

const BaseEnum BaseEnum::ONE; 
const BaseEnum BaseEnum::TWO; 
const DerivedEnum DerivedEnum::THREE; 

使用法:

BaseEnum e = DerivedEnum::THREE; 

if (e == DerivedEnum::THREE) { 
    std::cerr << "equal" << std::endl; 
} 
+0

私が見ることができる唯一の欠点は、より高いメモリ消費と、より多くのコード行が必要なことです。しかし、私はあなたの解決策を試みます。 – Knitschi

+0

私は '' '' '' BaseEnum :: i'''' publicと '' '' BaseEnum :: maxI''''をprivateにしました。 – Knitschi

+0

保護されたデフォルトのコンストラクタは、デフォルトのコンストラクタを必要とするサードパーティのマクロやテンプレートでenumを使用する必要がある場合に問題になります。 – Knitschi

0

あなたが拡張列挙型を作成するために、プロジェクトSuperEnumを使用することができます。

/*** my_enum.h ***/ 
class MyEnum: public SuperEnum<MyEnum> 
{ 
public: 
    MyEnum() {} 
    explicit MyEnum(const int &value): SuperEnum(value) {} 

    static const MyEnum element1; 
    static const MyEnum element2; 
    static const MyEnum element3; 
}; 

/*** my_enum.cpp ***/ 
const MyEnum MyEnum::element1(1); 
const MyEnum MyEnum::element2; 
const MyEnum MyEnum::element3; 

/*** my_enum2.h ***/ 
class MyEnum2: public MyEnum 
{ 
public: 
    MyEnum2() {} 
    explicit MyEnum2(const int &value): MyEnum(value) {} 

    static const MyEnum2 element4; 
    static const MyEnum2 element5; 
}; 

/*** my_enum2.cpp ***/ 
const MyEnum2 MyEnum2::element4; 
const MyEnum2 MyEnum2::element5; 

/*** main.cpp ***/ 
std::cout << MyEnum2::element3; 
// Output: 3