2016-08-15 23 views
1

純粋な仮想インターフェイスを提供する基本クラスがあります。私は、基本クラスへのポインタのリストに、派生クラスのオブジェクトへのポインタを格納する必要があります。派生クラスのテンプレート引数を使用する基本クラス

派生クラスは、テンプレートメカニズムを使用して作成されます。問題は、派生クラスだけが知っている型を返すための仮想インタフェースを用意したい場合、テンプレート引数として渡す必要があることです。ジレンマが始まるこれは...

template <typename ITEM> 
class base { 
public: 
virtual ITEM* get() = 0; 
}; 

template <typename ITEM> 
class derived : public base<ITEM>{ 
public: 
ITEM* get() {...}; 
}; 

である。しかしベースにテンプレートを使用するとき、私は、ベースポインタのリストを作成する場合でも、これを知っておく必要があります。もちろん

base* myList[10] = {derived1, derived2,...} 

私はしないでください私のリストを定義するときにそのタイプを知っている。だから私は何とか私の基本クラスのテンプレートを取り除く必要があります。

編集:このアプローチは、まったく有用なアプローチではなかったので、このアプローチを廃止しました。だからこの問題の解決策はありません。

+8

あなたは何とか 'myList'を設定したとしましょう。あなたはそれをどのように使用する予定ですか?あなたは 'myList [5] .get()'と呼ぶことができますが、どのような型であるのかわからないときはどうしますか? –

+0

たぶんあなたは 'dynamic_cast'をチェックすることができますが、テンプレートではうまくいくかどうかわかりませんが – Ceros

+0

これはあなたが持っているかもしれませんが、**間違った理由で(http: /meta.stackexchange.com/questions/66377/what-is-the-xy-problem)。 – StoryTeller

答えて

1

あなたが書いたコードは無効です。 1つのbaseタイプはJavaではパラメータ化されていませんが、数はbase<T>タイプです。本当に汎用的なオブジェクトのラッパーを取得する方法があり、これは「型消去」と呼ばれます。これは、例えばboost :: anyの実装で使用されます。

基本的には、仮想関数を持つテンプレート以外の基本クラスがあり、それを実装するテンプレート派生クラスを作成します。あなたはbaseは、純粋仮想関数を持っているため、派生型のTメンバーが可能になるので(インスタンス化することはできませんので、baseオブジェクトの配列を持つようにしたい場合は、ここで示した簡素化バージョンがない作業を行うことに注意してくださいスライスオフ)。

struct base; 
template<typename T> 
struct derived; 

struct base { 
    virtual ~base(); 

    // In this class we don't know about T, so we cannot use it 
    // Other operations that delegate to the derived class are possible, though 
    virtual std::size_t sizeofT() const = 0; 
    virtual const std::type_info& typeofT() const = 0; 

    // Since all you want is a pointer in "get", you could write it as a void* 
    virtual void* getPtr() = 0; 

    // Otherwise, we can implement this template function here that calls the virtual. 
    // Note that function templates cannot be virtual! 
    template<typename U> 
    U& getAs() { 
     // Verify that the type is the _same_ (no up/downcasts allowed) 
     // std::bad_cast is thrown here if U is not the same T used to build this object 
     derived<U>& meAsU = dynamic_cast<derived<U>&>(*this); 
     return meAsU.obj; 
    } 
}; 

template<typename T> 
struct derived : public base { 
    T obj; 
    // A couple of ctors to initialize the object, and the default copy/move ctors/op= 
    virtual ~derived(); 
    derived(const T& o) : obj(o) {} 
    derived(T&& o) : obj(std::move(o)) {} 

    std::size_t sizeofT() const override { 
     return sizeof(T); 
    } 
    const std::type_info& typeofT() const override { 
     return typeid(T); 
    } 
    void* getPtr() override { 
     return static_cast<void*>(&obj); 
    } 
}; 

あなたは、変数、または配列またはコンテナ内(ベクトル、リスト、など)として直接baseタイプを使用したい場合は、あなたが動的割り当てを必要とする - それは周りに2つの方法がありません。

  • あなたはbaseのポインタの配列を有することに自分自身を制限する場合は、上記のソリューションを使用することができます:あなたは、動的割り当ての責任をどこに配置するかで異なる2つの選択肢を持っています。例えば。 std::unique_ptr<base>の配列。指示されたオブジェクトのタイプはderived<something>です。

    base err1; // Error, abstract class (what would it contain?) 
    base err2 = derived<int>(2); // Still abstract class, and the int would be sliced off 
    std::unique_ptr<base> ok(new derived<int>(3)); // Works 
    
    std::vector<std::unique_ptr<base>> objects; 
    objects.push_back(std::make_unique(new derived<int>(5))); 
    objects.push_back(std::make_unique(new derived<std::string>(2))); 
    int& a = objects[0].getAs<int>(); // works 
    std::string& b = objects[1].getAs<std::string>(); // works too 
    std::string& bad = objects[1].getAs<double>(); // exception thrown 
    
  • それ以外の場合は、ベース/派生クラス自体で動的割り当てを実装する必要があります。これはboost :: anyやstd :: functionのようなクラスです。最も単純なanyオブジェクトは、私がここに示したbaseクラスのユニークなptrのラッパーで、適切な演算子の実装などです。次に、タイプany x = y;の変数を持つことができ、そのクラスはコンストラクタ内で必要なのはnew derived<Y>(y)です。

+0

これは 'dynamic_cast'の仕組みではありません。 –

+0

@ T.C。まあ、dynamic_castは_towards_ void *で使うことができるのを忘れていましたが、_from_ではありません。さて、「タイプ消去」の原則は、それなしで動作するので、私はgetAsの部分を削除します。とにかく、この素敵な小さなスニペットに感謝します。 –

+0

私は私の問題を解決するための別のアプローチを取ったが、それを読むことを楽しんだ! – binaryguy

関連する問題