iterator_traits
std::iterator_traits
はイテレータの型情報やカテゴリを表現する型特性クラスです。
#include <iterator>
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; // カテゴリ型
};
iterator_traitsを用いることで、イテレータに準拠した対象クラスから、イテレータの種類や参照値の型などの情報を取得することができます。
using T = std::iterator<std::input_iterator_tag, int>;
std::iterator_traits<T>::value_type; // int
std::iterator_traits<T>::pointer; // int*
std::iterator_traits<T>::reference // int&
std::iterator_traits<T>::iterator_category; // input_iterator_tag
取得された情報は適切なアルゴリズムへの分岐や適用の際に活用することができます。また対象のイテレータがiterator_traitsに準拠していない場合や、求めるカテゴリでない場合には、エラーの発生につなげることも可能となります。
iterator_traitsは特殊化により、ポインタ型への適用にも対応しています。
typename iterator_traits<int*>::value_type i = 9; // OK
typename iterator_traits<char*>::value_type c = 'c'; // OK
ただし、配列型には対応していません。
int a[] = {1, 2, 3};
typename iterator_traits<decltype(a)>::value_type x; // No type named 'value_type' in 'std::__1::iterator_traits<int [3]>'
イテレータ型やポインタ型以外の、とりわけiterator_traitsに準拠していない型についても同様のエラーが発生します。
struct X {};
typename iterator_traits<X>::pointer p; // No type named 'pointer' in 'std::__1::iterator_traits<X>'
iterator_traitsへ準拠する方法
std::iteratorの継承
std::iterator_traits
の特性を満たすクラスを宣言する方法として最も簡単な手段は、標準のイテレータ・クラス(std::iterator
)の継承ですが、本クラスはC++17以降の規格では非推奨の機能になる予定です。
#include <iterator>
struct Range : std::iterator<std::input_iterator_tag, int> {
value_type start, end;
};
typename std::iterator_traits<Range>::value_type i; // OK: typeof(i) == int
メンバ型の宣言
iterator_traitsは以下のメンバ型の宣言を必要とします。これらを独自に宣言することでiterator_traitsの要求する仕様を満たすことができます。
名称 | 役割 |
---|---|
difference_type | イテレータの減算演算時の型 |
value_type | イテレータの指す値型 |
pointer | イテレータのポインタ型 |
reference | イテレータの参照型 |
iterator_category | イテレータのカテゴリ型 |
宣言例は以下の通りです。
// メンバ型の宣言例(`std::iterator<std::input_iterator_tag, int>`相当)
struct Range {
using difference_type = ptrdiff_t;
using value_type = int;
using pointer = int*;
using reference = int&;
using iterator_category = std::input_iterator_tag;
};
iterator基本クラスの問題点
std::iteratorによる簡略化した宣言は便利な半面、分かりづらい挙動をとる場合があります。以下のようなテンプレート仮引数T
を指定するような例では、value_type
の名前解決が行えなるという問題を引き起こします。
template<typename T>
struct Iterator : std::iterator<std::input_iterator_tag, T> {
value_type data; // ERROR: Unknown type name 'value_type'
typename std::iterator<std::input_iterator_tag, T>::value_type data; // OK
};
次のように、自前で型を定義したほうが明確です。扱う側の混乱も少なくなります。
template<typename T>
struct Iterator {
using value_type = T;
};
iteratorの基本型を独自定義する
一応、廃止予定のstd::iterator
をシュミレートする方法も紹介しておきます。メンバ型の面倒な独自宣言を簡略化する用途として利用できます。
template<class Category, class T, class Diff = ptrdiff_t, class Ptr = T*, class Ref = T&> struct basic_iterator {
using difference_type = Diff;
using value_type = T;
using pointer = Ptr;
using reference = Ref;
using terator_category = Category;
};
struct Range : basic_iterator<std::input_iterator_tag, int> {
value_type start, end;
};