Pimpl и умные указатели

Порой понимаешь, что не знаешь каких-то банальных вещей. Сегодня на повестке дня Pimpl и умные указатели. Я почему-то был уверен, что forward class declaration можно делать только если класс используется в форме указателя или ссылки (#1), то есть тексте записывается в форме T* или T& (#2). Поэтому, когда мне надо было реализовать Pimpl, я не использовал умные указатели, так как с виду для них требуется полное определение класса.

A.h:

#include <memory>
class A_pimpl;
class A {
  
  std::unique_ptr<A_pimpl> p;
}

Я почему-то думал, что это не будет работать из-за неопределенности класса A_pimpl. И был сильно удивлен, попробовал и узнав, что на самом деле это прекрасно работает. То есть факт #1 не эквивалентен факту #2.

В A.cpp можно теперь спокойно писать:

#include "A.h"
#include "pimpl.h"

A::A() : p(new A_pimpl()) {}

Все выше сказанное также работает для std::shared_ptr (C++ 2011), boost::scoped_ptr и boost::shared_ptr.

Дополнение

Как меня поправили в комментариях, у класса A обязательно должен быть явно задан деструктор, причем его тело должно быть именно в A.cpp, а не в заголовочном файле. Иначе будет ошибка типа “error C2338: can’t delete an incomplete type”.

A.h:

#include <memory>
class A_pimpl;
class A {
  A();
  ~A();
  std::unique_ptr<A_pimpl> p;
}

и A.cpp:

#include "A.h"
#include "pimpl.h"

A::A() : p(new A_pimpl()) {}
A::~A() {}

Disclaimer

Комментарии