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
と、「D
にpointer
が定義されていればD::pointer
、そうでなければT*
」の部分。
どうやって実装しているのでしょうか?
glibcの実装を読んでみました。
これはTがリファレンス型ならばそれを取り除く、というテンプレートです。
T
が
R&
ならば
remove_reference<T>::type
は
R
に、そうでなければ
remove_reference<T>::type
は
T
になります。
面倒くさいので右辺値参照は適宜読み替えてもらうことにします。
T
がリファレンスではないときは簡単で、単純に
T
を
type
としてtypedefするだけです。
template <typename T> struct remove_reference { typedef T type; };
参照のときはどうするのかと思ったら、特殊化で実装されていました。
template <typename T> struct remove_reference<T&> { typedef T type; };
なるほど。
簡単に言うと
template<typename T, typename D> class unique_ptr
に対して、
unique_ptr<T, D>::pointer
は
D::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::pointer
が
unique_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 $