読者です 読者をやめる 読者になる 読者になる

茂加部珈琲店

主にtech関連のメモ置き場です

type traits(型特性)クラスの利用

またまたc++勉強ネタ。

traitsクラス

traitsクラスは、templateを利用したテクニックの一つです。
標準ライブラリや、boostなどで多用される手法の一つでもあります。
今回は、traitsクラスの基本的な利用法を勉強したいと思います。

traitsクラス

type traits(型特性)は、型に関する特性を表現するために利用されるクラスです。
traitsには色々なバリエーションがありますが、まず例として、STLiterator_traitsクラステンプレートを見てみましょう.

traitsを利用した例 std::iterator_traits

std::iterator_traitsは、イテレータに関する型特性を表現するためのクラスで、以下のような構成をしています.

以下のコードはcpprefjp - C++日本語リファレンスから引用しました。

namespace std {
  template <class Iterator>
  struct iterator_traits {
    using difference_type   = typename Iterator::difference_type;
    using value_type        = typename Iterator::value_type;
    using pointer           = typename Iterator::pointer;
    using reference         = typename Iterator::reference;
    using iterator_category = typename Iterator::iterator_category;
  };

  // ポインタに対する特殊化
  template <class T>
  struct iterator_traits<T*> {
    using difference_type   = ptrdiff_t;
    using value_type        = T;
    using pointer           = T*;
    using reference         = T&;
    using iterator_category = random_access_iterator_tag;
  };

  template<class T>
  struct iterator_traits<const T*> {
    using difference_type   = ptrdiff_t;
    using value_type        = T;
    using pointer           = const T*;
    using reference         = const T&;
    using iterator_category = random_access_iterator_tag;
  };
}

コードを見ると、iterator_traitsは単にtypedefされた型情報を格納しているだけで、メンバを持っていません.
使用者は、適切にこのテンプレートを特殊化することによって、iterator用に作られたアルゴリズムを利用することができるようになります

traits活用: タグ・ディスパッチ

std::iterator_traitsには、タグ・ディスパッチのために利用する iterator_categoryという型を持っています
iterator_categoryには、タグ(tag)と呼ばれる空クラスを指定します
標準で用意されているクラスは以下

  • std::input_iterator_tag
  • std::output_iterator_tag
  • std::forward_iterator_tag
  • std::bidirectional_iterator_tag
  • std::random_access_iterator_tag

さて、このiterator_categoryをどう使うかというと、関数が引数の型によって選択できることを利用します

//こんな感じでディスパッチさせる
template <typename InputIterator>
void foo(InputIterator itr, std::input_iterator_tag){
}

template <typename OutputIterator>
void foo(OutputIterator itr, std::output_iterator_tag){
}

template <typename Iterator>
void foo(Iterator itr){
  foo(itr, 
      typename std::iterator_traits<Iterator>::iterator_category());
}

また、std::forward_iterator_tagなどは他のイテレータタグを継承しています
タグが変換可能であれば呼び出しを行えるようになるので、継承がうまく利用されていますね

以上、type traitsの基本でした。この他にもstatic関数と組み合わせたり、traitsには様々な活用法があるみたいです
いろんなライブラリをよんで勉強していきたいですね!