类型不完整的 std::unique_ptr 不会编译

新手上路,请多包涵

我将 pimpl-idiom 与 std::unique_ptr 一起使用:

 class window {
  window(const rectangle& rect);

private:
  class window_impl; // defined elsewhere
  std::unique_ptr<window_impl> impl_; // won't compile
};

但是,我在 <memory> 的第 304 行收到关于使用不完整类型的编译错误:

sizeof ‘对不完整类型’ uixx::window::window_impl ‘的无效应用

据我所知, std::unique_ptr 应该能够与不完整的类型一起使用。这是 libc++ 中的错误还是我在这里做错了什么?

原文由 user1203803 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 735
2 个回答

以下是一些 std::unique_ptr 类型不完整的示例。问题在于破坏。

如果将 pimpl 与 unique_ptr 一起使用,则需要声明一个析构函数:

 class foo
{
    class impl;
    std::unique_ptr<impl> impl_;

public:
    foo(); // You may need a def. constructor to be defined elsewhere

    ~foo(); // Implement (with {}, or with = default;) where impl is complete
};

因为否则编译器会生成一个默认值,为此它需要一个完整的 foo::impl 声明。

如果您有模板构造函数,那么即使您不构造 impl_ 成员,您也会被搞砸:

 template <typename T>
foo::foo(T bar)
{
    // Here the compiler needs to know how to
    // destroy impl_ in case an exception is
    // thrown !
}

在命名空间范围内,使用 unique_ptr 也不起作用:

 class impl;
std::unique_ptr<impl> impl_;

因为编译器必须知道如何销毁这个静态持续时间对象。一种解决方法是:

 class impl;
struct ptr_impl : std::unique_ptr<impl>
{
    ~ptr_impl(); // Implement (empty body) elsewhere
} impl_;

原文由 Alexandre C. 发布,翻译遵循 CC BY-SA 4.0 许可协议

使用自定义删除器

问题是 unique_ptr<T> 必须调用析构函数 T::~T() 在它自己的析构函数,它的移动赋值运算符,和 unique_ptr::reset() 成员函数(仅)。但是,必须在几个 PIMPL 情况下(已经在外部类的析构函数和移动赋值运算符中)调用(隐式或显式)这些。

正如在另一个答案中已经指出的那样,避免这种情况的一种方法是将 所有 需要 unique_ptr::~unique_ptr()unique_ptr::operator=(unique_ptr&&)unique_ptr::reset() 的操作移到源文件中pimpl 助手类实际上是定义的。

然而,这相当不方便,并且在某种程度上违背了 pimpl idoim 的观点。一个更简洁的解决方案可以避免使用 _自定义删除器_,并且只将其定义移动到 pimple helper 类所在的源文件中。这是一个简单的例子:

 // file.h
class foo
{
    struct pimpl;
    struct pimpl_deleter { void operator()(pimpl*) const; };
    std::unique_ptr<pimpl,pimpl_deleter> m_pimpl;
  public:
    foo(some data);
    foo(foo&&) = default;             // no need to define this in file.cc
    foo&operator=(foo&&) = default;   // no need to define this in file.cc
  //foo::~foo()          auto-generated: no need to define this in file.cc
};

// file.cc
struct foo::pimpl
{
  // lots of complicated code
};
void foo::pimpl_deleter::operator()(foo::pimpl*ptr) const { delete ptr; }

除了单独的删除器类,您还可以使用自由函数或 static 成员 foo

 class foo {
    struct pimpl;
    static void delete_pimpl(pimpl*);
    using deleter = void(&)(pimpl*);
    std::unique_ptr<pimpl,deleter> m_pimpl;
  public:
    foo(some data);
};

原文由 Walter 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题