何故に特性が"non-intrusive"であることが重要か

A key feature of traits templates is that they're non-intrusive: they allow us to associate information with arbitrary types, including built-in types and types defined in third-party libraries, Normally, traits are specified for a particular type by (partially) specializing the traits template.

何故,ここでイタリックで強調されているように特性が"non-intrusive"であることが重要なのか.まず,"non-intrusive"という言葉の意味を明らかにするために,あえて"intrusive"なtraitsの定義を考えて,それと比較してみます.

class my_class
{
public:
  // "intrusive"な特性の定義
  typedef hoge_tag trait_A;
  typedef huga_tag trait_B;

  // 以下色々定義
};

これはこれで型の特性の定義となり,以下のような形で汎用関数で利用することが可能です.

template
void generic_func(T x)
{
  // "intrusive"な特性の利用
  typedef typename T::trait_A trait_A;
  typedef typename T::trait_B trait_B;

  // 以下,trait_Aやtrait_Bによってディスパッチするなどの処理
}

ただし,このような"intrusive"な特性の定義と利用には2つの大きな欠点があります.

  • 組み込み型にはネストしたtypedef(あるいはその他のネストした定義)が定義できないため,上のgeneric_funcは組み込み型に対して用いることが出来ない
  • サードパティが提供したクラスが必ずしもgeneric_funcが要求するようなtypedefを定義しているとは限らない

従って,これら2つに対して上のようなgeneric_funcを用いるためには「全体の」ラッパを書かなければならなくなります.そして,このラッパを書くというコストはしばしば高くつきます.(特に小さなモジュールのためだけにわざわざ全体のラッパを書くのはアホらしくてやってられません.)
例えば,組み込み型であるintを上のgeneric_funcに突っ込むために,以下のようなラッパをわざわざ作ってそれでintをラップしてからgeneric_funcに渡さなくてはならなくなります.

class int_wrapper_for_generic_func
{
public:
  typedef special_tag_for_int trait_A; // intに対して特性を特殊化したいとする
  typedef huga_tag trait_B;

  // 以下generic_funcが要求するoperatorの定義全部!(boost::operatorsで楽は出来ますが・・・)
  // あ,そういえばコンストラクタも要るのか・・・('A`)ノ マンドクセー
};

・・・上のようなことを延々やらないとならなくなります.
そこで特性の"non-intrusive"な定義と利用が重要になります.これによって組み込み型やサードパティが提供する型に対してgeneric_funcを利用するために,generic_funcが要求する「最低限の」"non-intrusive"な特性の特殊化を行うだけで事足ります."non-intrusive"な特性の定義と利用は以下のような形になります.

// これはデフォルトで提供されるtraits
template
struct traits
{
  // "intrusive" -> "non-intrusive"
  typedef typename T::trait_A trait_A;
  typedef typename T::trait_B trait_B;
};

template
void generic_func(T x)
{
  // "non-intrusive"な特性の利用
  typedef typename traits::trait_A trait_A;
  typedef typename traits::trait_B trait_B;

  // 以下,上のgeneric_funcと同じ
}

ここで,クライアントがintをgeneric_funcに突っ込みたいと思ったら,以下を定義するだけですみます.

// generic_funcにintを突っ込むためにクライアントが定義すべき特殊化
template<>
struct traits
{
  typedef special_tag_for_int trait_A;
  typedef huga_tag trait_B;
};

このように,特性の"non-intrusive"な特性の定義と利用は汎用関数の再利用の可能性と簡便性を向上させるためになくてはならないことが分かります.
次回あたり,以前自分が直面した"non-intrusive"な特性の特殊化の実例について書いてみようと思います.