2011-02-05 15 views
2

テンプレートの引数が関連している場合、あるオブジェクトを別のオブジェクトにキャストできるようにクラステンプレートを実装できますか?C++でテンプレートクラスの共分散を実装する方法は?

struct Base {}; 
struct Derived : Base {}; 

template <typename T> class Foo { 
    virtual ~Foo() {} 
    virtual T* some_function() = 0; 
}; 

Foo<Derived>* derived = ...; 
Foo<Base>* base = derived; 

ここでの付加的な問題は、fooがT &とT *を返す関数を含むインターフェースとして使用される抽象クラスであるということですので、私:ここでの考え方を示すためexapleがある(もちろん、それはコンパイルされません)テンプレートコピーコンストラクタを実装できません。

std::list<Derived> l; 
MyIterator<Base> it(l.begin()); 

UPD:

私はそれが多型になりたい消去を入力するために、任意のSTLのイテレータを保持し、ほかにできるユニバーサルイテレータクラスを書いている、つまり、私はこのような何かを書くことができますそれは私の間違いでしたが、MyIteratorを実装するためにFoo *にFoo *をキャストする必要は実際にはありませんでした。

+1

私は 'dynamic_cast'があなたがここでしたいことをすることを非常に疑っています。 – aschepler

+0

あなたは正しいです、それはNULLを返しています:) – lizarisk

答えて

4

テンプレートの引数は、指しているオブジェクトの内容とは関係ありません。これがうまくいく理由はありません。

struct Base { }; 
struct Derived : Base {}; 

template<typename T> struct A { int foo; }; 
template<> struct A<Base> { int foo; int bar; }; 

A<Derived> a; 
A<Base> *b = &a; // assume this would work 
b->bar = 0; // oops! 

を説明するためにあなたは最終的に本当にaに存在しない整数barにアクセスします!


これでもう少し詳しい情報を提供しましたので、完全に違うことをしたいと思います。ここではいくつかのスターターは、次のとおりです。

template<typename T> 
struct MyIterator : std::iterator<...> { 
    MyIterator():ibase() { } 
    template<typename U> 
    MyIterator(U u):ibase(new Impl<U>(u)) { } 
    MyIterator(MyIterator const& a):ibase(a.ibase->clone()) 

    MyIterator &operator=(MyIterator m) { 
    m.ibase.swap(ibase); 
    return *this; 
    } 

    MyIterator &operator++() { ibase->inc(); return *this; } 
    MyIterator &operator--() { ibase->dec(); return *this; } 
    T &operator*() { return ibase->deref(); } 
    // ... 

private: 
    struct IBase { 
    virtual ~IBase() { } 
    virtual T &deref() = 0; 
    virtual void inc() = 0; 
    virtual void dec() = 0; 
    // ... 

    virtual IBase *clone() = 0; 
    }; 
    template<typename U> 
    struct Impl : IBase { 
    Impl(U u):u(u) { } 
    virtual T &deref() { return *u; } 
    virtual void inc() { ++u; } 
    virtual void dec() { --u; } 
    virtual IBase *clone() { return new Impl(*this); } 
    U u; 
    }; 

    boost::scoped_ptr<IBase> ibase; 
}; 

その後、あなたはあなたがany_iteratorに見てみたいことがあります

MyIterator<Base> it(l.begin()); 
++it; 
Base &b = *it; 

として使用することができます。少し運があれば、そのテンプレートをあなたの目的に使うことができます(私はそれをテストしていません)。

+0

ああ、とても簡単!私はほとんど同じコードを書いていますが、IBaseとImplをグローバルクラス(ネストされていません)として書いています - これはImplがIbase :: value_type>を継承していたので、それはTとは異なっていたので、ポインタは共変ではありませんでした。今私はMyIteratorの中に入れて、すべてが動作します:) – lizarisk

+0

'MyIterator'の' operator == 'を実装する方法に関するアイデアは、MyIterator :: Impl ::他のものは 'MyIterator :: Impl :: iterator>'ですか? – aschepler

+0

@aschepler私はany_iteratorのドキュメントが正しいと思います:あなたはそれを実装するのが不可能な場合に、(同じimplsを比較できるようにし、typeidを使ってチェックすることを許可するだけで)安全にするかどちらかです。あるいは、型のIDが異なると認識している場合にのみ、一方の側をstatic_castにします。その場合、両方のイテレータが同じ基本レイアウトを持ち、別の型として解釈されたときに「動作する」と仮定する必要があります。 –

1

Foo<Derived>は、Foo<Base>を継承しないため、前者を後者に変換することはできません。また、あなたの前提が間違っています:dynamic_castが失敗します。

Foo<Derived>インスタンスをコピーするFoo<Base>のインスタンスの新しいオブジェクトを作成することができますが、これは現在あなたが探しているものだと思います。

4

この種の関係はテンプレートでは「組み込み」ではないことが他の回答から指摘されていますが、この種の関係に対応する機能を構築できることに注意してください。例えば、

class A { ... }; 
class B : public A { ... }; 

与えboost::shared_dynamic_cast and friendsあなたはboost::shared_ptr<A>boost::shared_ptr<B>の間にキャストしましょう。

このような実装を行う場合は、MyIteratorがサポートする操作に注意する必要があります。例えば、MyIterator<Base>(std::list<Derived>::iterator)のあなたの例を使用して、あなたは

*myIter = someBaseValue; 

はコンパイルべきではない、例えば、 operator*()の左辺値バージョンを持つべきではありません。

+0

イテレータの例starter-implementationは、逆参照演算子からlvaluesを返します。私はそれが賢明なことだと思う。 –

+0

プロトタイプのイテレータ(ポインタ)はスライスされた代入を許可します(基本クラスにパブリック代入がある場合)。クラステンプレートがそれをサポートし、それを慎重にプログラマに任せていくのは妥当だと思います。 – aschepler

+0

。おそらくより良い議論は、iteratorクラスでそれを防止するのではなく、 "継承と割り当てのミキシングを避ける"ことです。この例は実際にはスライスしていません。ベースに派生したものではなく、派生したものにベースを割り当てています。 –

関連する問題