2016-08-06 7 views
2

私は型消去(voidキャスト)を使用して、1対1のマップに複数の基本型(float、bool、intなど)を格納しています。元のデータ型を再解釈する1つの方法は、代わりにペア/共用体/クラスを使用して、値を型識別子(たとえば、対)で保存することです。 BoostなしでC++ 11を使って識別子なしで基本型を解決するきれいな方法はありますか?型識別子なしのキャストvoidを再解析する

std::map<int, void *> m_mymap; 
// avoid std::map<int, pair<void, MyEnum> > m_mymap; 

template <class T> void setValue<T>(int i_key, T i_val) 
{ 
    m_mymap[i_key] = reinterprete_cast<void *>(i_val); 
} 

template <class T> T getValue<T>(int i_key) 
{ 
    return reinterprete_cast<T>(i_val); 
} 

doSomeWork() 
{ 
    for (const auto & elem : m_mymap) 
    { 
     auto val = some_magical_cast<???>(elem.second) // resolve type without a nasty switch against MyEnum 
     // do some work 
    } 
} 

編集:無効、使用するアップデートの質問*

+0

「void」には、必要なデータが含まれていません。何も含まれていません。 – aschepler

+1

本質的にvoid *マップを使用する考え方に似てキャストしました lukemtesta

+0

あなたは 'boost :: any'を使いたくない理由はありますか?それはまさにその仕事の道具のようだ。 – templatetypedef

答えて

3
auto val = some_magical_cast<???>(elem.second) // resolve type without a nasty switch against MyEnum 

C++は静的型付き言語です。すべての型はコンパイル時に知られていなければなりません。あなたがしようとしているのは、マップに入れられた内容に基づいて、ランタイムで型を決定することです。

これはC++では何もできません。 valには、実行時コードを実行することによって決定されるタイプではなく、単一の特定のタイプが必要です。ループの繰り返しはすべて、同じタイプにする必要があります。ユニオンまたはvariantを使用すると、実行時に決定されるいくつかのタイプのうちの1つを保存できます。しかし、それらが格納するタイプのセットはコンパイル時に固定されています。

auto valが何らかの形で一連の異なるタイプになることがあっても、void*には、それが指しているタイプを回復するために使用できる情報が含まれていません。 boost/std::anyには、このタイプを回復する機能もありません。特定のタイプを尋ねなければなりません(間違ったタイプを尋ねるとanyが失敗し、はあなたにUBを与えます)。

だから、これは不可能です。

2

@NicolBolasで正しく説明されているように、あなたはできません。

#include<map> 
#include<utility> 
#include<iostream> 

class C { 
public: 
    template<typename T> 
    void accept(int k, T v) { std::cout << k << " " << v << std::endl; } 
}; 

using P = void(*)(C &, int, void *); 

std::map<int, std::pair<P, void*>> m; 

template<typename T> 
void proto(C &c, int k, void *v) { 
    c.accept(k, *static_cast<T*>(v)); 
} 

template <class T> 
void set(int k, T *v) { 
    m[k] = { &proto<T>, v }; 
} 

void get(C &c, int k) { 
    auto p = m[k]; 
    p.first(c, k, p.second); 
} 

int main() { 
    int i = 42; 
    double d = 1.2; 

    set(0, &i); 
    set(1, &d); 

    C c; 
    for(auto &&k: m) { 
     get(c, k.first); 
    } 
} 

それは例の追加クラス(Cが必要です。

は、実行可能なアプローチ/回避策として、次の例のように、を派遣ダブルの考えに基づいて技術を使用することができます)。
異なる種類の異なるメソッドを追加できます(例として、テンプレートメソッドではなく、intまたはdoubleのいずれかを受け入れる2つのメソッドを使用できます)。
このようにして、一般化された動作だけでなく、多くの種類固有の動作を持つことができます。

もちろん、それはより複雑で無料ではありませんが、そうでなければあなたが探しているものを得ることができません。

+0

作業は今c.acceptに入りますか? get get(int){c.doWork}は私の意見では読みにくくなっています。 – lukemtesta

+0

@lukemtesta仕事は 'c 'になります。もちろん、「受け入れる」。あなたはもっと 'get 'を必要としません、あなたはもっと' accept'を必要とします。残念なことに、このソリューションは読みやすくすることを目的としたものではなく、達成したいものと似たようなことをすることが目的です。とにかく、二重ディスパッチは広く使われているテクニックです。 – skypjack

関連する問題