2016-07-05 6 views
1

さまざまなテンプレートパラメータを持つオブジェクトを持つコンテナを持つことができるのだろうかと思います。テンプレートパラメータのないテンプレートクラスのコンテナ

私はこのような何かを達成しようとしている:新しい派生クラスが必要なとき、私は私の現在のプロジェクトは少し余分なコーディングで、できるだけ柔軟かつ動的になりたい

#include <iostream> 
#include <list> 

template <class T> 
class base 
{ 
    public: 
     T val; 
     base(T newVal): val(newVal) {}; 
}; 

class derived : public base<int> 
{ 
    public: 
     derived(int newVal): base(newVal) {}; 
}; 

int main (void) 
{ 
    std::list < base<?> > base_collection; 
    return 0; 
} 

、そして私の現在そのようなリストが存在することが重要になります。

正確にこれを達成するためによく使用される有益かつクリーンな方法はありますか?

+0

"私の現在の実装では、そのような[異質な]リストが存在することが重要になります"。ああ。それは反パターンです。あなたはそのデザインを捨てる必要があります。 –

+0

できません。 'base 'は 'base 'とはまったく異なるタイプ*です。とにかく、基本クラスの変数に派生クラスオブジェクトを格納することはできません。 – immibis

答えて

2

可能な実装を使用することになり派遣ダブル:

#include <iostream> 
#include <list> 

struct visitor; 

struct dispatchable { 
    virtual void accept(visitor &v) = 0; 
}; 

template <class> 
struct base; 

struct visitor { 
    template<typename T> 
    void visit(base<T> &); 
}; 

template <class T> 
struct base: dispatchable { 
    T val; 
    base(T newVal): val(newVal) {}; 
    void accept(visitor &v) override { v.visit(*this); } 
}; 

struct derivedInt : base<int> { 
    derivedInt(int newVal): base(newVal) {}; 
}; 

struct derivedDouble : base<double> { 
    derivedDouble(double newVal): base(newVal) {}; 
}; 

template<> 
void visitor::visit(base<int> &) { 
    std::cout << "int" << std::endl; 
} 

template<> 
void visitor::visit(base<double> &) { 
    std::cout << "double" << std::endl; 
} 

int main (void) { 
    visitor v{}; 
    std::list <dispatchable*> coll; 
    coll.push_back(new derivedInt{42}); 
    coll.push_back(new derivedDouble{.42}); 
    for(auto d: coll) d->accept(v); 
} 

この方法で、あなたが紹介したい新しいbase<T>種類を扱う専門的な関数を定義するだけです。私はあなたが別の方法でbase<T>の各専門分野を扱いたいはず

template<> 
void visitor::visit(base<char> &) { 
    std::cout << "char" << std::endl; 
} 

注意:あなたがbase<char>を使用する場合の例として、
は、あなたが定義する必要があります。それ以外の場合は、汎用メンバ関数visitor::visitを定義して、特殊化を削除するだけで十分です。


サイドノート:裸のポインタを使用しないでください。
これは一例です。プロダクションコードでは、代わりにスマートポインタを使用します。

+0

これは完璧です、ありがとう。 – reign

+0

これは、 'process event'のように、クラスごとに関連する操作が1つだけであれば許容されます。 –

+0

@JohanLundberg OPは、それぞれ 'T'のために' base 'を処理しようとしています。あなたの答えにコメントで述べたように、彼は* valを使うことを望んでいます。彼はそれを使うことができるこの方法。この場合、ダブルディスパッチがうまくいきます。何が問題なの? – skypjack

3

なぜこれを行う必要があるのか​​、リストの要素に対してどのような操作を実行するのかはっきりしません(btw、代わりにstd vectorの使用を検討してください)。私はそのベースから継承しますが、一般的な非テンプレートベースクラスを作るお勧め:あなたは、ベクターでそれらすべてを置くつもりなら、すべての後

struct mainbase { 
    virtual ~mainbase() = default; 
}; 

template <class T> 
class base : public mainbase 
{ 
    public: 
     T val; 
     base(T newVal): val(newVal) {}; 
}; 


class derived : public base<int> 
{ 
    public: 
     derived(int newVal): base(newVal) {}; 
}; 

int main (void) 
{ 
    std::list < std::unique_ptr<mainbase>> > base_collection; 
    return 0; 
} 

、あなたが最も可能性の高いあなたの操作の共通セットを必要としますこれらのオブジェクトで実行できます。それらをmainbaseに入れてください。

@BenjaminLindleyが指摘しているように、多型による値を持つことはできません。そのため、ポインタ(such as unique_ptr)std::unique_ptr<mainbase>を使用します。

C++では、代わりにstd::anyの提案がありますが、正しいタイプのコンテンツを取得するためには特定のキャストを実行する必要があります。

+1

あなたはポインタ(スマートまたはそれ以外)を保存したいでしょう。 –

+0

どのようにキャストせずにmainbaseオブジェクトからvalにアクセスしますか? – reign

+0

@BenjaminLindley。なぜこの場合ポインタを使用するのですか? –

関連する問題