использовать пользовательское средство удаления
Проблема в том, что unique_ptr<T>
должен вызывать деструктор T::~T()
в своем собственном деструкторе, своем операторе назначения перемещения и unique_ptr::reset()
функции-члене (только). Однако они должны вызываться (неявно или явно) в нескольких ситуациях PIMPL (уже в деструкторе внешнего класса и в операторе присваивания перемещения).
Как уже указывалось в другом ответе, один из способов избежать этого - переместить все операции, требующие unique_ptr::~unique_ptr()
, unique_ptr::operator=(unique_ptr&&)
и unique_ptr::reset()
, в исходный файл, где находится вспомогательный класс pimpl. на самом деле определяется.
Однако это довольно неудобно и в некоторой степени противоречит самой сути сутенерства. Гораздо более чистое решение, позволяющее избежать всего, что заключается в использовании пользовательского удалителя и перемещении его определения только в исходный файл, в котором находится вспомогательный класс pimple. Вот простой пример:
// 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*);
std::unique_ptr<pimpl,[](pimpl*ptr){delete_pimpl(ptr);}> m_pimpl;
};