traits

  • 特性。テンプレート引数に渡された型、定数によって動作を切り替える。


例えば、shared_ptr< T >でvoidを渡すと、

shared_ptr< void > p;

operator*()の戻り値の型がvoid&になるのでコンパイルエラーになってしまいます。

template < typename T >
class shared_ptr
{
...
    T& operator*()const{ return *ptr; }
...
};
  • Tがvoid以外→T&
  • Tがvoid→void

のように、Tの型によって戻り値の型を選択したいわけです。
ちなみにtemplate内の関数の{...}は呼び出し処理を書かない限りコンパイルされないので、voidでreturnを記述していてもコンパイルエラーになりません。

では、さっそくoperator*()の戻り値の型を決定するための特性クラスを作ってみましょう。

sahred_ptr_traits

//テンプレート引数T&をreference_tに置き換える。
//    shared_ptr_traits< T >::reference_t == T&
template< typename T >
struct shared_ptr_traits
{
    typedef T& reference_t;
};

これだけではshared_ptr_traits< void >::reference_tがvoid&になってしまうので、Tがvoidのときだけ、voidをreference_tとします。

//テンプレート引数T&をreference_tに置き換える。
//    shared_ptr_traits< T >::reference_t == T&
template< typename T >
struct shared_ptr_traits
{
    typedef T& reference_t;
};

//Tがvoidの時の特殊化バージョン
//    shared_ptr_traits< void >::reference_t == void
template<>
struct shared_ptr_traits< void >
{
    typedef void reference_t;
};

operator*()を書き換えましょう。

template < typename T >
class shared_ptr
{
...
    typename shared_ptr_traits< T >::reference_t operator*()const{ return *ptr; }
...
};

長くて読みにくいのでtypedefしましょう。

template < typename T >
class shared_ptr
{
...
    typedef typename shared_ptr_traits< T >::reference_t reference_t;
...
    reference_t operator*()const{ return *ptr; }
...
};

テンプレート引数にある型を渡すと、内部で加工されて別の型が返ってくるんですね。
型専用の関数のようです。
これがいわゆる「テンプレートメタプログラミング」です。
boostでも幅広く使われていますね。

次回からテンプレートメタプログラミングも扱っていきたい思います。