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 $