pimpl イディオムの主な利点は、ヘッダーではなく実装ファイル内のデータ メンバーを非表示にすることです。ただし、コンパイラが必要に応じてテンプレートをインスタンス化できるようにするには、ヘッダーでテンプレートを完全に定義する必要があります。この場合、テンプレート化されたクラスに pimpl イディオムを使用する利点はありますか?
5 に答える
pimpl イディオムは、テンプレート化されたクラスで使用される場合、実際には何も隠しませんが、スローしないスワップを簡単に記述できます (ただし、C++11 のムーブ セマンティクスでは、これはあまり問題になりません)。
厳密には pimpl のイディオムではないかもしれないが、知っておく必要があるほど似ているケースが 1 つあります。これは、タイプセーフでないバージョンに対してタイプセーフなテンプレート ラッパーを使用することです。
class MapBase
{
public:
void* getForKey(const std::string & k);
void setForKey(const std::string & k, void * v);
...
};
template<typename T>
class MyMap
{
public:
T* getForKey(const std::string &k) { return (T*)base_.getForKey(k); }
void setForKey( const std::string &k, const T* v) { base_.setForKey(k, T*v); }
private:
MapBase base_;
};
これで、 の使用はMyMap<T>
MapBase の内部にさらされる必要がなくなり、これらの関数の根幹の実装を 1 つだけ取得できます。また、デカップリングをさらに強化するために、MapBase を抽象基本クラスにすることも検討します。
私が言ったように、それは正確にはにきびではありませんが、同じ問題の多くを同様の方法で解決します.
大規模なプロジェクトでは、翻訳単位を分離するだけで、吹き出物の十分な理由になります。これはテンプレートでも機能します:
// main interface
template <typename> struct MyImpl;
class TheInterface
{
MyImpl<int> * pimpl;
};
// implementation
#include "MyImpl.hpp" // heavy-weight template library
// TheInterface implementation
慣用句を少し拡張すると、場合によっては少なくとも少しは理解できると思います。テンプレートでは、すべての操作が必ずしもテンプレート パラメーターに依存する必要はありません。Impl
したがって、次のような pimpl イディオムを使用するクラスから継承できます。
struct FooPimpl;
class FooImpl {
protected:
FooPimpl* pimpl;
public:
void myCommonInterfaceMethod();
};
template <typename T> class Foo : public FooImpl {
// stuff that depends on T
};
もちろん、これは状況によって大きく異なります。しかし、pimpl イディオムがテンプレート クラスのコンテキストで機能していることがわかります。
それはまだかなり使用可能です - テンプレートであり、PIMPL の解決と実装に非常に使用可能な auto または shared ポインターを検討してください。それが最善の解決策であるかどうかは、問題によって異なります。
コンパイラが必要に応じてテンプレートをインスタンス化できるようにするには、テンプレートをヘッダーで完全に定義する必要があります。
テンプレートのメンバーとインターフェイスを宣言してから、型の cpp ファイルで#include
必要な特殊化の定義を行い、そこで使用できます。