unique_ptr のデリータ指定

25日の記事のコメントで default_delete の特殊化で解決する方法を教えていただきました。ありがとうございます。

個人的メモとして今の所知ってる unique_ptr のデリータ指定方法を纏めておきます。


1.関数オブジェクトを渡す

一番メジャーな方法。関数オブジェクトを作って default_delete の代わりに指定します。

struct master_voice_deleter
{
    void operator()( IXAudio2MasteringVoice* voice ) const
    {
        voice->DestroyVoice();
    }
};

std::unique_ptr< IXAudio2MasteringVoice, master_voice_deleter >   ptr;


2. default_delete の特殊化で解決

default_delete をテンプレート特殊化すればいいだけです。std名前空間のものは完全特殊化しかできないので注意。

namespace std
{
    template<>
    struct default_delete<IXAudio2MasteringVoice>
    {
        void operator()( IXAudio2MasteringVoice* voice ) const
        {
            voice->DestroyVoice();
        }
    };
};

std::unique_ptr< IXAudio2MasteringVoice >   ptr;


3. 関数を渡す

関数はテンプレート引数に関数の型が欲しいので、decltype を使って型を渡し、インスタンス化の際にその型にあった関数を第2引数に渡します。

void master_voice_delete( IXAudio2MasterVoice* voice )
{
    voice->DestroyVoice();
}

std::unique_ptr< IXAudio2MasteringVoice, decltype( &master_voice_delete ) >   ptr( ... , master_voice_delete );

3つ上げましたけど、同じ型のものであれば削除の方法は変わらないはずなので、default_delete の特殊化が一番簡単な気がします。
その都度に応じて削除方法が異なる場合は関数オブジェクトを指定するメジャーな方法を使う。
関数を指定する場合は、既に削除関数が用意されているときには有効だと思われます。例えば、FILE構造体など。

std::unique_ptr< FILE, decltype( &fclose ) >  fp( fopen(...), fclose );

最後に、HANDLE 型など既にポインタ型となっているものをどうするか。
std::remove_pointerを使ってポインタを取り除いた型にします。

struct handle_deleter
{
    void operator()( HANDLE p ) const
    {
        CloseHandle( p );
    }
};

std::unique_ptr<std::remove_pointer<HANDLE>::type,handle_deleter> ptr( CreateEvent(...) );

[参考]
std::unique_ptrとstd::shared_ptr - 名古屋313の日記

[修正]
コメントから、unique_ptr側でデリータを呼び出す前にNULLチェックがされているのでデリータではNULLチェックする必要ありませんでしたね。修正しました。