【发布时间】:2011-09-26 23:09:54
【问题描述】:
据我了解,pimpl 习惯用法的主要好处是将数据成员隐藏在实现文件中而不是标题中。但是,模板需要在头文件中完全定义,以便编译器按需实例化它们。在这种情况下,对模板类使用 pimpl 习语有什么好处吗?
【问题讨论】:
标签: c++ templates pimpl-idiom
据我了解,pimpl 习惯用法的主要好处是将数据成员隐藏在实现文件中而不是标题中。但是,模板需要在头文件中完全定义,以便编译器按需实例化它们。在这种情况下,对模板类使用 pimpl 习语有什么好处吗?
【问题讨论】:
标签: c++ templates pimpl-idiom
虽然 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 所需的特化定义并在那里使用它们。
【讨论】: