ライブラリのユーザーが外部依存関係 (boost など) を必要としないように pimpl イディオムを使用したいのですが、メソッドがヘッダーにある必要があるため、クラスがテンプレート化されている場合は不可能に思えます。代わりに何かできることはありますか?
3 に答える
クラスがテンプレート化されている場合、ユーザーは基本的にクラスをコンパイルする必要があり(これは、最も広く使用されているC ++実装では文字通り当てはまります)、外部の依存関係が必要です。
最も簡単な解決策は、クラスの実装の大部分を非テンプレート基本クラス(またはあるクラスのカプセル化されたメンバーオブジェクト)に配置することです。そこでモジュール隠しの問題を解決します。
次に、型の安全性を追加するために、テンプレートから派生した(または囲む)クラスを記述します。
たとえば、最初のアクセス時に割り当てる驚くべき機能を提供するテンプレートがあるとします(必要なコピーコンストラクタ、割り当て、デストラクタを省略します)。
template <class T>
class MyContainer
{
T *instance_;
public:
MyContainer() : instance_(0) {}
T &access()
{
if (instance_ == 0)
instance_ = new T();
return *instance_;
}
};
「ロジック」を非テンプレート基本クラスに分離したい場合は、非テンプレートの方法で動作をパラメーター化する必要があります。つまり、仮想関数を使用します。
class MyBase
{
void *instance_;
virtual void *allocate() = 0;
public:
MyBase() : instance_(0) {}
void *access()
{
if (instance_ == 0)
instance_ = allocate();
return instance_;
}
};
次に、外側のレイヤーにタイプ認識を追加できます。
template <class T>
class MyContainer : MyBase
{
virtual void *allocate()
{ return new T(); }
public:
T &access()
{ return *(reinterpret_cast<T *>(MyBase::access())); }
};
つまり、仮想関数を使用して、テンプレートがタイプに依存する操作を「入力」できるようにします。明らかに、このパターンは、隠す努力に値するビジネスロジックがある場合にのみ本当に意味があります。
ソース ファイルでテンプレートを明示的にインスタンス化できますが、これは、テンプレートの種類がわかっている場合にのみ可能です。それ以外の場合は、テンプレートに pimpl イディオムを使用しないでください。
このようなもの :
header.hpp :
#ifndef HEADER_HPP
#define HEADER_HPP
template< typename T >
class A
{
// constructor+methods + pimpl
};
#endif
ソース.cpp:
#include "header.hpp"
// implementation
// explicitly instantiate for types that will be used
template class A< int >;
template class A< float >;
// etc...
2 つの一般的な解決策があります。
インターフェースはいくつかの型に依存している
T
が、より弱い型付けの実装 (例えば、void*
ポインターを直接使用するもの、または型消去を経由するもの) に委ねる、または特定の非常に限られた数のタイプのみをサポートします。
2番目の解決策は、たとえばchar
/にwchar_t
依存するものに関連しています。
最初の解決策は、C++ テンプレートの初期の頃には非常に一般的でした。当時のコンパイラは、生成されたマシン コードの共通点を認識するのが苦手で、いわゆる「コードの肥大化」をもたらしていたからです。今日、テンプレート化されたソリューションは、ランタイム ポリモーフィズムに依存するソリューションよりもマシン コードのフットプリントが小さいことがよくあります。もちろん、YMMV。
乾杯 & hth.,