据我所知,pimpl惯用法的主要优点是将数据成员隐藏在实现文件中,而不是头文件中。然而,模板需要在头文件中完全定义,以便编译器能够按需实例化它们。在这种情况下,对于一个带有模板的类来说,使用pimpl惯用法是否有任何优势呢?
据我所知,pimpl惯用法的主要优点是将数据成员隐藏在实现文件中,而不是头文件中。然而,模板需要在头文件中完全定义,以便编译器能够按需实例化它们。在这种情况下,对于一个带有模板的类来说,使用pimpl惯用法是否有任何优势呢?
虽然在模板类中使用pimpl技巧并不能真正隐藏任何内容,但它确实可以让你轻松编写非抛出式交换函数(尽管在C++11移动语义下,这已经不是一个问题了)。
// main interface
template <typename> struct MyImpl;
class TheInterface
{
MyImpl<int> * pimpl;
};
// implementation
#include "MyImpl.hpp" // heavy-weight template library
// TheInterface implementation
有一种情况可能并不严格属于pimpl习惯用法,但足够类似以值得了解。那就是在非类型安全的版本上使用类型安全模板包装器。
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 的内部,而且你只会得到这些函数实现的一个版本。我还考虑将 MapBase 设为抽象基类,以使解耦更加强大。
正如我所说的,它并不完全是 pimpl,但它以类似的方式解决了许多相同的问题。
我认为,如果你稍微扩展一下这个习语,在某些情况下你至少可以从中获得一些好处。在模板中,并不是每个操作都必须依赖于模板参数。因此,你可以从一个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。是否是最佳解决方案取决于问题。
为了让编译器能够按需实例化模板,需要在头文件中完全定义模板。
您可以声明模板的成员和接口,然后在类型的cpp文件中,您可以#include
所需的特化定义并在那里使用它们。