C++ 如何为pimpl使用unique_ptr?

C++ 如何为pimpl使用unique_ptr?,c++,c++11,pimpl-idiom,unique-ptr,C++,C++11,Pimpl Idiom,Unique Ptr,下面是我在尝试将unique_ptr用于pimpl时看到的情况的简化。我选择unique_ptr是因为我真的希望类拥有指针——我希望pimpl指针和类的生命周期相同 无论如何,这里是标题: #ifndef HELP #define HELP 1 #include <memory> class Help { public: Help(int ii); ~Help() = default; private: class Impl; std::unique_pt

下面是我在尝试将unique_ptr用于pimpl时看到的情况的简化。我选择unique_ptr是因为我真的希望类拥有指针——我希望pimpl指针和类的生命周期相同

无论如何,这里是标题:

#ifndef HELP
#define HELP 1

#include <memory>

class Help
{

public:

  Help(int ii);
  ~Help() = default;

private:

  class Impl;
  std::unique_ptr<Impl> _M_impl;
};

#endif // HELP
我可以把它们编译成一个库。但是当我尝试在测试程序中使用它时,我得到

ed@bad-horse:~/ext_distribution$ ../bin/bin/g++ -std=c++0x -o test_help test_help.cpp Help.cpp
In file included from /home/ed/bin/lib/gcc/x86_64-unknown-linux-gnu/4.7.0/../../../../include/c++/4.7.0/memory:86:0,
                 from Help.h:4,
                 from test_help.cpp:3:
/home/ed/bin/lib/gcc/x86_64-unknown-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/unique_ptr.h: In instantiation of 'void std::default_delete<_Tp>::operator()(_Tp*) const [with _Tp = Help::Impl]':
/home/ed/bin/lib/gcc/x86_64-unknown-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/unique_ptr.h:245:4:   required from 'void std::unique_ptr<_Tp, _Dp>::reset(std::unique_ptr<_Tp, _Dp>::pointer) [with _Tp = Help::Impl; _Dp = std::default_delete<Help::Impl>; std::unique_ptr<_Tp, _Dp>::pointer = Help::Impl*]'
/home/ed/bin/lib/gcc/x86_64-unknown-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/unique_ptr.h:169:32:   required from 'std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = Help::Impl; _Dp = std::default_delete<Help::Impl>]'
Help.h:6:7:   required from here
/home/ed/bin/lib/gcc/x86_64-unknown-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/unique_ptr.h:63:14: error: invalid application of 'sizeof' to incomplete type 'Help::Impl'
ed@bad-horse:~/ext\u distribution$../bin/bin/g++-std=c++0x-o test\u help test\u help.cpp help.cpp
在/home/ed/bin/lib/gcc/x86_64-unknown-linux-gnu/4.7.0/../../../../../../../include/c++/4.7.0/memory:86:0中包含的文件中,
来自帮助。h:4,
来自test_help.cpp:3:
/home/ed/bin/lib/gcc/x86_64-unknown-linux-gnu/4.7.0/../../../../../../include/c++/4.7.0/bits/unique_ptr.h:在“void std::default_delete::operator()(_Tp*)const[with _Tp=Help::Impl]的实例化中:
/home/ed/bin/lib/gcc/x86_64-unknown-linux-gnu/4.7.0/../../../../../../include/c++/4.7.0/bits/unique_ptr.h:245:4:从“void std std::unique_ptr::reset(std::unique_ptr::pointer)[with _tphelp::Impl;_Dp=std::default\u delete;std::unique_ptr::pointer=Help::Impl*]中需要
/home/ed/bin/lib/gcc/x86_64-unknown-linux-gnu/4.7.0/../../../../../../../include/c++/4.7.0/bits/unique\u ptr.h:169:32:必须来自“std::unique\u ptr::~unique\u ptr()[带_Tp=Help::Impl;_Dp=std::default\u delete]”
帮助。h:6:7:从这里开始需要
/home/ed/bin/lib/gcc/x86_64-unknown-linux-gnu/4.7.0/../../../../../../../include/c++/4.7.0/bits/unique\u ptr.h:63:14:错误:“sizeof”对不完整类型“Help::Impl”的应用无效
这是一个众所周知的问题。我试着跟着

我的问题是,如果我将Help::Impl声明放在一个标题中,它似乎会消除pimpl的任何优势。类布局对用户可见。定义是隐藏的,但是我可以通过帮助类和私有成员来实现。另外,包括Impl的声明带来了新的头文件,我希望将它们分开保存


我错过了什么?人们在Impl声明中放了什么?放在哪里?我是在帮你还是做错了?啊

我相信您的test\u help.cpp实际上看到了您声明为默认的
~help()
析构函数。在该析构函数中,编译器也尝试生成
unique_ptr
析构函数,但它需要
Impl
声明

因此,如果您将析构函数定义移动到Help.cpp,那么这个问题就应该消失了

--编辑-- 您也可以在cpp文件中将析构函数定义为默认值:

Help::~Help() = default;
请注意以下定义:

std::unique_ptr可以为不完整的类型T构造,例如为了便于在pImpl习惯用法中用作句柄。如果使用默认删除器,则T必须在调用删除器的代码点处完成,这发生在std::unique_ptr的析构函数、移动赋值运算符和重置成员函数中


好的,在标题中,我刚刚声明了dtor以寻求帮助。然后在实现文件中,我将Help::~Help()=default;呜呜!!!谢谢一切正常!智能指针(包括
unique_ptr
)需要用于不完整的类型。“deleter”函数是在构造时分配的,因此指针的用户不需要知道如何删除它。如果这解决了问题,则意味着他的
unique\u ptr
实现被破坏。看来我的评论只适用于
shared\u ptr
,因此
unique\u ptr
确实是零成本的。糟糕的是,没有一种等价的
unique\u ptr
形式可以正确处理不完整的类型;在标题中内联了dtor。我发现有趣的是,while=delete和=default看起来很相似,但实际上它们却大不相同。前者是一个声明,后者是一个定义。另请参见和。尽管这是一个老问题,但可能需要指出的是,使用
unique\u ptr
实现的PImpl应该包装成这样的格式,以确保完全正确。@jdehesa谢谢,我将propagate_const视为解决某些API尴尬问题的解决方案。我几乎想知道unique_ptr是否在这种意义上默认被破坏。似乎propagate_const应该是内置的,或者至少是默认值。
Help::~Help() = default;