використовувати спеціальний делетер
Проблема полягає в тому, що він unique_ptr<T>
повинен викликати деструктора T::~T()
у власному деструкторі, його операторі призначення присвоєння руху та unique_ptr::reset()
функції члена (лише). Однак їх потрібно викликати (неявно або явно) у кількох ситуаціях PIMPL (вже у деструкторі зовнішнього класу та операторі призначення присвоєння).
Як вже зазначалося в іншу відповідь, один з способів уникнути цього, щоб перемістити всі операції, що вимагають unique_ptr::~unique_ptr()
, unique_ptr::operator=(unique_ptr&&)
іunique_ptr::reset()
в вихідний файл , в якому фактично визначено клас Pimpl помічник.
Однак це досить незручно і певною мірою заперечує саму точку pimpl idoim. Набагато більш чисте рішення, яке дозволяє уникнути всього, що полягає у використанні користувальницького делетера, і лише перемістити його визначення у вихідний файл, де живе клас помічника прищів. Ось простий приклад:
// 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;
};