unique_ptr はクラスの正確なサイズを知らないといけない?

最近悩んでいること。

ちょっと古いですが、[C++11時代におけるクラスの書き方](http://dev.activebasic.com/egtra/2011/12/02/451/) と言う記事があって、ふむふむなるほど、と思うわけですが、困ってしまうことがあります。

A と言うクラスと、APrivate と言うクラスがあって、Aは外部に公開するクラスで、APrivateは外部に公開したくない、Aのみが使うクラスです。

# 昔の書き方

```cpp:a.h
#ifndef __A_H
#define __A_H

class APrivate;

class A {
public:
    A();
    virtual ~A();
    int func();
private:
    APrivate *d;
};

#endif // __A_H
```

```cpp:a.cc
#include "a.h"

class APrivate {
public:
    int someData;
};

A::A() : d(new APrivate) {
    d->someData = 5;
}

A::~A() {
    delete d;
}

int A::func() {
    return d->someData;
}
```
外に見せない内部実装やデータはAPrivateが持ち、Aはそのポインタだけ持ちます。
このやり方の利点は、たとえばAが外部のライブラリに依存していたとして、そのライブラリで定義される型を持つ必要があるときに、AではなくAPrivateのメンバにすることによって、a.hに外部ライブラリのヘッダを書かなくて済むようになります。
(例えば、sys/select.h の fd_set とか)

# 推奨される書き方
* デストラクタは virtual = default とする
* リソースの解放が必要な場合は、ラップした型を作りメンバにする

ですが、ラップした型を外に見せたくないのです。
そのような場合、昔の書き方でポインタで持っているところを、unique_ptr にすれば良いのではないかと考えました。
unique_ptr にすれば、デストラクタを書かなくてもAが解放されるときに一緒にAPrivateも解放されるはずです。

と、言うわけでunique_ptrを使うように書き換えてみる。

```cpp:a2.h
#ifndef __A_H
#define __A_H

#include <memory>

class APrivate;

class A {
public:
    A();
    virtual ~A() = default;
    int func();
private:
    std::unique_ptr<APrivate> d;
};

#endif // __A_H
```
```cpp:a2.cc
#include "a2.h"

class APrivate {
public:
    int someData;
};

A::A() : d(std::unique_ptr<APrivate>(new APrivate)) {
    d->someData = 5;
}

int A::func() {
    return d->someData;
}
```

このようにすると、a2.cc自体のコンパイルは成功するけど、いざ別のファイルからa2.hをincludeして、Aのインスタンスを作ろうとすると、エラーになります。

```cpp:amain.cc
#include "a2.h"

int main(int argc, char *argv[]) {
    A a;
    return 0;
}
```


```shell-session
In file included from amain.cc:1:
In file included from ./a2.h:4:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/memory:2424:27: error:
      invalid application of 'sizeof' to an incomplete type 'APrivate'
            static_assert(sizeof(_Tp) > 0, "default_delete can not delet...
                          ^~~~~~~~~~~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/memory:2625:13: note:
      in instantiation of member function
      'std::__1::default_delete<APrivate>::operator()' requested here
            __ptr_.second()(__tmp);
            ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/memory:2593:46: note:
      in instantiation of member function 'std::__1::unique_ptr<APrivate,
      std::__1::default_delete<APrivate> >::reset' requested here
    _LIBCPP_INLINE_VISIBILITY ~unique_ptr() {reset();}
                                             ^
./a2.h:11:13: note: in instantiation of member function
      'std::__1::unique_ptr<APrivate, std::__1::default_delete<APrivate>
      >::~unique_ptr' requested here
    virtual ~A() = default;
            ^
./a2.h:6:7: note: forward declaration of 'APrivate'
class APrivate;
      ^
1 error generated.
```
どうも、デフォルトのdeleterの中のassertで、sizeof()しようとするけどAPrivateの中身がわからないのでエラーになるようです。
classだって言ってるんだからサイズチェックなんかしないでただdeleteしてくれれば良いのになあ。

# とりあえず動くようにする
unique_ptrにはcustom deleterが指定できるそうなので、チェックなしでdeleteするdeleterを作ってやればビルドは通りました。

```cpp:a3.h
#ifndef __A_H
#define __A_H

#include <memory>

class APrivate;

class A {
public:
    A();
    virtual ~A() = default;
    int func();
private:
    typedef void (*APrivateDeleter)(APrivate *p);
    std::unique_ptr<APrivate, APrivateDeleter> d;
};

#endif // __A_H
```
```cpp:a3.cc
#include "a3.h"

class APrivate {
public:
    int someData;
};

static void deleteAPrivate(APrivate *p) {
    delete p;
}

A::A() : d(std::unique_ptr<APrivate, APrivateDeleter>(new APrivate, deleteAPrivate)) {
    d->someData = 5;
}

int A::func() {
    return d->someData;
}
```

ビルドは通って、なんとなく動いていそうなのですが、ここまでしてAのデストラクタをdefaultにすることに意味があるかどうか、と言う所で悩んでいます。

何かもっと簡単にいける方法はないのかなあ・・・。