据我了解,pimpl 习惯用法的主要好处是将数据成员隐藏在实现文件中而不是标题中。但是,模板需要在头文件中完全定义,以便编译器按需实例化它们。在这种情况下,将 pimpl 习惯用法用于模板类有什么好处?
5 回答
虽然 pimpl 习惯用法在模板类中使用时并没有真正隐藏任何内容,但它确实允许您轻松编写非抛出交换(尽管使用 C++11 移动语义,这不是一个问题)。
在大型项目中,单独解耦翻译单元就是 pimpl 的充分理由。这甚至适用于模板:
// 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 习惯用法在模板类上下文中工作。
它仍然非常有用 - 考虑一个自动或共享指针,它是一个模板,非常适用于解决和实现 PIMPL。是否是最好的解决方案取决于问题。
模板需要在头文件中完全定义,以便编译器按需实例化它们。
您可以声明模板的成员和接口,然后在您的类型的 cpp 文件中,您可以#include
对专业化进行必要的定义并在那里使用它们。