2012-03-30 27 views
2

多型列挙型? C++で多型列挙型

、私たちはしばしば、古いコードは新しい コードを処理できるようにするために多型を使用して - 例えば、限り、我々は 機能が期待するインターフェイスをサブクラスとして、私たちは、新しいクラスに渡し、それを期待することができます を正しく動作させるには、新しいクラスが存在する前に記述されたコードを使用します。 残念ながら、列挙型では、 が時々あなたがしたいと思っているにもかかわらず、あなたは本当にこれを行うことはできません。たとえば、プログラムの設定を管理していて、そのすべてを 列挙型の値として格納した場合、 という列挙型を持つことができます。これは、 の列挙型です。他のすべての列挙型は継承できます。設定リスト内のすべての 新しい列挙型を格納します。リストため、異なる種類の 値が含まれていることに注意してください、あなたがテンプレートを使用することはできません。)

あなたが行動のこの種を必要とする場合は、あなたが店に強制しています列挙型は の整数とし、タイプキャストを使用してそれらを取得して、 の値を目的の設定に割り当てます。また、キャストが であることを確認するのに役立つ のdynamic_castのメリットも得られません。間違った値が に格納されないという事実に頼らざるを得ません。

私はC++ programming tutorialから引用しています。

誰でももっと多面的な列挙型がどのように機能するか、いくつかの例を説明できますか? テンプレートがある場合は?簡単に言えば

+2

これについて移動する正しい方法は、容器の中にこのような何かを設定する機能を格納するだろうです少なくとも最も単純な意味で、仕事をする。'enum'は基本的に基本的な整数型です。多態性の 'enum'を使いたい場所であれば、テンプレートを使ったより洗練されたソリューションが得られるはずです。 – Chad

+5

多態型列挙体はあまり意味がありません。あなたがそれらを必要と思うならば、あなたのデザインは不器用です。あなたが本当にそれらを必要とするなら、Boost.Variantは動作するはずです。 – Pubby

+0

@Chadでは、ちょっとしたコードスニペットの例を理解しやすくなりますか? – Matteo

答えて

3

は、enumは単にインスタンスの名前付き定数値である。上記の例で

enum Settings 
{ 
    setting_number_0, 
    setting_number_1, 
    setting_number_2,  
}; 

列挙値は、0から始まり、増加するように、setting_number_Xは、単に値Xための名前付き定数であります単調に。

これを維持すると、ある種のコンテナでは整数の基本的な格納タイプが得られますが、やはり幾分タイプセーフである可能性があります。

std::vector<Setting> app_settings; 

// this works 
app_settings.push_back(setting_number_0); 

// this is a compile time failure, even though the underlying storage 
// type for Setting is an integral value. This keeps you from adding 
// invalid settings types to your container (like 13 here) 
app_settings.push_back(13); 

// but you also cannot (directly) add valid setting values (like 1) 
// as an integral, this is also a compile time failure. 
app_settings.push_back(1); 

さらに、特定の設定タイプを追加して、それらをすべてコンテナに保存するとします。あなたは、単一のコンテナ内のすべての設定を維持したい場合

enum DisplaySettings 
{ 
    // ... 
}; 

enum EngineSettings 
{ 
    // ... 
}; 

さて、あなたはない安全することができます。すべての積分値をstd::vector<int>またはそれに類するコンテナに格納することはできますが、どのような積分タイプがどのような設定列挙に属しているのかを判別することができません。また、型が異なるため、単一の型の安全なコンテナに格納することはできません。私はそれが多型は、 `「ドンをenums`ことを引用したテキストから明らかだと思う

#include <vector> 
#include <iostream> 

// This is our "base class" type so we can store lots of 
// different setting types in our container 
class setting_action 
{ 
public: 
    // we enable the setting by calling our function 
    void enable_setting() 
    { 
     setting_function_(this); 
    } 

protected: 
    // This is a function pointer, and we're using it to get some 
    // compile time polymorphism 
    typedef void (*setting_function_type)(setting_action* setting); 

    // these can only be constructed by derived types, and the derived 
    // type will provide the polymorhpic behavior by means of the 
    // above function pointer and based on the derived type's handler 
    setting_action(setting_function_type func) 
     : setting_function_(func) 
    { 
    } 

public: 
    ~setting_action() 
    { 
    } 

private: 
    setting_function_type setting_function_; 
}; 

// This is the derived type, and where most of the magic 
// happens. This is templated on our actual setting type 
// that we define below  
template <class Setting> 
class templated_setting_action 
    : public setting_action 
{ 
public: 
    templated_setting_action(Setting setting) 
     : setting_action(&templated_setting_action::enable_setting) 
     , setting_(setting) 
    { 
    } 

    // This function catches the "enable_setting" call from 
    // our base class, and directs it to the handler functor 
    // object that we've defined 
    static void enable_setting(setting_action* base) 
    { 
     templated_setting_action<Setting>* local_this = 
     static_cast<templated_setting_action<Setting>*>(base); 

     local_this->setting_(); 
    } 

private: 
    Setting setting_; 
}; 

// this is just a shorthand way of creating the specialized types 
template <class T> 
setting_action* create_specialized_setting_action(T type) 
{ 
    return 
     new templated_setting_action<T>(type); 
} 

// Our actual settings: 
// this one displays the user name  
struct display_user_name 
{ 
    void operator()() 
    { 
     std::cout << "Chad.\n"; 
    } 
}; 

// this one displays a short welcome message  
struct display_welcome_message 
{ 
    void operator()() 
    { 
     std::cout << "Ahh, the magic of templates. Welcome!\n"; 
    } 
}; 

// now, we can have one container for ALL our application settings 

std::vector<setting_action*> app_settings; 

int main() 
{ 
    // now we can add our settings to the container... 
    app_settings.push_back(create_specialized_setting_action(display_user_name())); 
    app_settings.push_back(create_specialized_setting_action(display_welcome_message())); 

    // and individually enable them 
    app_settings[0]->enable_setting(); 
    app_settings[1]->enable_setting(); 

    // also, need to delete each setting to avoid leaking the memory 
    // left as an exercise for the reader :) 
    return 0; 
}