質問
これは以前に尋ねられたような気がしますが、SOで見つけることができず、Googleで役に立つものを見つけることもできません。多分<!> quot; covariant <!> quot;私が探している言葉ではありませんが、この概念は関数の共変戻り型に非常に似ているため、おそらく正しいと思います。私がやりたいことは次のとおりです。コンパイラエラーが発生します。
class Base;
class Derived : public Base;
SmartPtr<Derived> d = new Derived;
SmartPtr<Base> b = d; // compiler error
これらのクラスは完全に肉付けされていると仮定します...あなたはアイデアを得ると思います。何らかの不明な理由により、SmartPtr<Derived>
をSmartPtr<Base>
に変換できません。これはC ++や他の多くの言語では普通であることを思い出しますが、現時点ではその理由を思い出せません。
私の根本的な質問は、この割り当て操作を実行する最良の方法は何ですか?現在、私はSmartPtr
からポインターを引き出し、明示的に基本型にアップキャストしてから、適切な型の新しい<=>にラップしています(これは、自家製の< =>クラスは侵入参照カウントを使用します)。それは長くて厄介です、特に<=>をさらに別のオブジェクトでラップする必要がある場合...ショートカットはありますか?
解決
コピーコンストラクターと割り当て演算子の両方が、異なるタイプのSmartPtrを取得し、ポインターを一方から他方にコピーできるようにする必要があります。型に互換性がない場合、コンパイラは文句を言い、互換性がある場合は問題を解決しました。このようなもの:
template<class Type> class SmartPtr
{
....
template<class OtherType> SmartPtr(const SmartPtr<OtherType> &blah) // same logic as the SmartPtr<Type> copy constructor
template<class OtherType> SmartPtr<Type> &operator=(const SmartPtr<OtherType> &blah) // same logic as the SmartPtr<Type> assignment operator
};
他のヒント
SmartPtr<Base>
およびSmartPtr<Derived>
は、SmartPtr
テンプレートの2つの異なるインスタンス化です。これらの新しいクラスは、Base
とDerived
が継承する継承を共有しません。したがって、あなたの問題。
この割り当て操作を実行する最良の方法は何ですか?
SmartPtr<Base> b = d;
代入演算子を呼び出しません。これにより、コピーアクターが呼び出され(ほとんどの場合、コピーは省略されます)、次のように記述されます。
SmartPtr<Base> b(d);
SmartPtr<OtherType>
を取得して実装するコピーアクターを提供します。代入演算子についても同様です。 SmartPtrのセマンティクスを念頭に置いて、copy-ctorとop =を書き出す必要があります。
テンプレートは共変ではありません。それは良いことです。次の場合に何が起こるか想像してください:
vector<Apple*> va;
va.push_back(new Apple);
// Now, if templates were covariants, a vector<Apple*> could be
// cast to a vector<Fruit*>
vector<Fruit*> & vf = va;
vf.push_back(new Orange); // Bam, we just added an Orange among the Apples!
実行しようとしていることを実現するには、SmartPointerクラスにテンプレート化されたコンストラクターが必要です。このコンストラクターは、別のSmartPointerまたは別のタイプのポインターを受け取ります。 boost :: shared_ptrを見ると、まさにそれができます。
template <typename T>
class SmartPointer {
T * ptr;
public:
SmartPointer(T * p) : ptr(p) {}
SmartPointer(const SmartPointer & sp) : ptr(sp.ptr) {}
template <typename U>
SmartPointer(U * p) : ptr(p) {}
template <typename U>
SmartPointer(const SmartPointer<U> & sp) : ptr(sp.ptr) {}
// Do the same for operator= (even though it's not used in your example)
};
SmartPtr
クラスに依存します。 SmartPtr<T>
を取るコピーコンストラクター(または、場合によっては代入演算子)があり、Tが構築された型である場合、SmartPtr<T1>
はSmartPtr<T2>
とは無関係であるため、機能しませんT1とT2が継承によって関連付けられている場合でも。
ただし、SmartPtrにTOther
を受け入れるテンプレートパラメーターSmartPtr<TOther>
を持つ templatized コピーコンストラクター/割り当て演算子がある場合は、動作するはずです。
SmartPtrクラスを制御できると仮定すると、解決策はテンプレートコンストラクターを提供することです。
template <class T>
class SmartPtr
{
T *ptr;
public:
// Note that this IS NOT a copy constructor, just another constructor that takes
// a similar looking class.
template <class O>
SmartPtr(const SmartPtr<O> &src)
{
ptr = src.GetPtr();
}
// And likewise with assignment operator.
};
T型とO型に互換性がある場合、動作します。そうでない場合は、コンパイルエラーが発生します。
最も簡単なことは、次のように別のSmartPtrへの自動変換を提供することだと思います:
template <class T>
class SmartPtr
{
public:
SmartPtr(T *ptr) { t = ptr; }
operator T * () const { return t; }
template <class Q> operator SmartPtr<Q> () const
{ return SmartPtr<Q>(static_cast<Q *>(static_cast<T *>(* this))); }
private:
T *t;
};
この実装は、変換演算子テンプレートがスマートポインターのセマンティクスを知る必要がないという意味で堅牢であるため、参照カウントを複製する必要がないなどのことに注意してください。