home / uni / uniqptr unique_ptrの実装

 C++ Reference見るとこう書いてあります。

template <class T, class D = default_delete<T>> class unique_ptr;

### Member types
pointer
    remove_reference<D>::type::pointer, if this type exists
    T*, otherwise

 気になったのがremove_referenceと、「Dpointerが定義されていればD::pointer、そうでなければT*」の部分。 どうやって実装しているのでしょうか? glibcの実装を読んでみました。

home / uni / uniqptr remove_reference<T>

 これはTがリファレンス型ならばそれを取り除く、というテンプレートです。 TR& ならば remove_reference<T>::typeRに、そうでなければ remove_reference<T>::typeT になります。 面倒くさいので右辺値参照は適宜読み替えてもらうことにします。

  T がリファレンスではないときは簡単で、単純に Ttype としてtypedefするだけです。

template <typename T>
struct remove_reference {
    typedef T type;
};

 参照のときはどうするのかと思ったら、特殊化で実装されていました。

template <typename T>
struct remove_reference<T&> {
    typedef T type;
};

なるほど。

home / uni / uniqptr unique_pointer::pointer

 簡単に言うと template<typename T, typename D> class unique_ptr に対して、 unique_ptr<T, D>::pointerD::pointer があれば D::pointer 、 なければ T* です。 どうやって D::pointer の有無を判断しているのでしょう? これはこんな感じでした。

template <typename T, typename D = default_delete<T> >
class unique_ptr {
    template <typename U>
    static typename U::pointer __test(typename U::pointer*);

    template <typename Up>
    static T* __test(...);

public:
    typedef decltype(__test<D>(0)) type;
};

 なんじゃこりゃぁ?! という感じです。 type__test 関数の戻り値の型をdecltypeで取っています。 __test 関数のテンプレート引数は正確にはremove_referenceしていますが、簡単にするため省略しています。 __test 関数はすぐ上で宣言だけされていて実体はありません。 必要なのは戻り値の型だけで、実際に呼び出すことはないから。 ふたつの引数型で多重定義されていて、片方は引数として U::pointer * を取り、戻り値の型は U::pointer です。 もう一方は、引数は「なんでもよい」ことになっていて、 戻り値の型は T* です。

 この関数に引数として0を渡すと、もし、 U::pointer があれば、0はヌルポインタと解釈され、引数として ... よりも優先順位の高い U::pointer * が選択されます。 したがって、その戻り値型 U::pointerunique_ptr<T, D>::pointer として採用されます。

  U::pointer がなければ ... が選択されて、その戻り値型 T*unique_ptr<T, D>::pointer として採用されます。

 コンパイラも大変だ。 余談ですけど、unique_ptrってuniq_ptrじゃだめだったんでしょうか? ptrの部分は略してるのにね。


Copyright (C) 2015 akamoz.jp

$Id: unique_ptr.htm,v 1.2 2015/10/31 01:54:06 you Exp $