有没有办法结合编译器防火墙(Pimpl)和默认可复制性的好处?

the*_*ill 5 c++ pimpl-idiom

假设我有一个私有成员的类,这是该类客户端不关心的实现细节.这个类是一个值类型,我们希望它是可复制的,例如

#include <boost/bimap.hpp>  // some header that pulls in many other files

class MyClass {
public:
    MyClass() {}
    ...
private:
    boost::bimap<Key,Value>   table;
};
Run Code Online (Sandbox Code Playgroud)

现在,MyClass的每个客户端都被迫引入了许多不需要的升级头,增加了构建时间.但是,该课程至少是可复制的.

如果我们引入编译器防火墙(Pimpl idiom),那么我们可以将#include依赖项移动到cpp文件,但是由于规则5,我们现在必须做更多的努力工作:

// no extra #includes - nice
class MyClass {
public:
    MyClass() {}
    // ugh, I don't want this, just make it copyable!
    MyClass(const MyClass& rhs);
    MyClass(MyClass&& rhs);
    MyClass& operator=(const MyClass& rhs);
    MyClass& operator=(MyClass&& rhs);
    ~MyClass() {}
    ...
private:
    std::unique_ptr<MyClassImpl>  impl;
};
Run Code Online (Sandbox Code Playgroud)

是否有一种技术可以获得编译器防火墙的好处,但保留可复制性,以便我不需要包含5的规则样板?

Rei*_*ica 9

我认为这里最好的解决方案是构建自己的深度复制智能指针.如果你只是将它定制为存储Pimpls,那应该不会太困难:

template <class P>
class pimpl_ptr
{
  std::unique_ptr<P> p_;

public:
  ~pimpl_ptr() = default;

  pimpl_ptr(const pimpl_ptr &src) : p_(new P(*src.p_)) {}
  pimpl_ptr(pimpl_ptr &&) = default;
  pimpl_ptr& operator= (const pimpl_ptr &src) { p_.reset(new P(*src.p_)); return *this; }
  pimpl_ptr& operator= (pimpl_ptr &&) = default;

  pimpl_ptr() = default;
  pimpl_ptr(P *p) : p_(p) {}

  P& operator* () const { return *p_; }
  P* operator-> () const { return &**this; }

  // other ctors and functions as deemed appropriate
};
Run Code Online (Sandbox Code Playgroud)

只记录它不支持指向基类子对象,并且你已经设置好了.您可以通过不给它一个构造函数来获取指针来强制执行此操作,并通过make_pimpl以下方式强制构造:

template <class P>
class pimpl_ptr
{
  // as above, P* ctor is private
private:
  pimpl_ptr(P *p) : p_(p) {}

  template <class T, class... Arg>
  friend pimpl_ptr<T> make_pimpl(Arg&&... arg);
};

template <class T, class... Arg>
pimpl_ptr<T> make_pimpl(Arg&&... arg)
{
  return pimpl_ptr<T>(new T(std::forward<Arg>(arg)...));
}
Run Code Online (Sandbox Code Playgroud)

  • `~pimpl_ptr()= default;`导致未定义的行为,因为模板类型`P`有一个非平凡的析构函数,编译器看不到它 (2认同)