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

C++0x - コンセプト(concept)

コンセプトとは、簡単に言えばテンプレートをもっと分かりやすく、もっと強力に使うためのものだ



コンセプトには以下の3つの機能がある

Concept Difinitions
 型に対する要求を定義する


Where Clauses
 型Tに必要なコンセプトを指定する


Concept Map
 既存の型(組み込み型等)がどのようにコンセプトの要求を満たすかを定義する



次のようなコンセプトを定義したとする

auto concept LessThanComparable<class T> {
    bool operator<(const T&, const T&);
}

そして、このコンセプトの要求を満たさなければならないクラス/関数を定義する

template <LessThanComparable T>
const T& min(const T& lhs, const T& rhs)
{
    return lhs < rhs ? lhs : rhs;
}


こうしてもいい(こっちの書き方だと、コンセプトを&&で複数指定できる)

template <class T>
requires LessThanComparable<T>
const T& min(const T& lhs, const T& rhs)
{
    return lhs < rhs ? lhs : rhs;
}

すると、min関数はLessThanComparableの要求「Tはbool operator<(const T&, constT&)を持っていなければならない」
ということを明示化することができる


TがLessThanComparableの要求を満たさない場合は
「クラスTはbool operator<(const T&, constT&)を持っていません」
のような、人間がまともに読めるエラーメッセージを出力してくれる





std::listの要素をソートするプログラムを例にしよう

list<int> li;
sort(li.begin(), li.end());

このプログラムをコンパイルした際にVC++8.0が出力するエラーメッセージは以下のようになる

main.cpp
C:\Program Files\Microsoft Visual Studio 8\VC\include\algorithm(3112) : error C2784: 'reverse_iterator<_RanIt>::difference_type std::operator -(const std::reverse_iterator<_RanIt> &,const std::reverse_iterator<_RanIt2> &)' : テンプレート 引数を 'const std::reverse_iterator<_RanIt> &' に対して 'std::list<_Ty>::_Iterator<_Secure_validation>' から減少できませんでした
with
[
_Ty=int,
_Secure_validation=true
]
C:\Program Files\Microsoft Visual Studio 8\VC\include\xutility(1856) : 'std::operator -' の宣言を確認してください。
.\main.cpp(11) : コンパイルされたクラスの テンプレート のインスタンス化 'void std::sort::_Iterator<_Secure_validation>>(_RanIt,_RanIt)' の参照を確認してください
with
[
_Ty=int,
_Secure_validation=true,
_RanIt=std::list::_Iterator
]
C:\Program Files\Microsoft Visual Studio 8\VC\include\algorithm(3112) : error C2784: 'reverse_iterator<_RanIt>::difference_type std::operator -(const std::reverse_iterator<_RanIt> &,const std::reverse_iterator<_RanIt2> &)' : テンプレート 引数を 'const std::reverse_iterator<_RanIt> &' に対して 'std::list<_Ty>::_Iterator<_Secure_validation>' から減少できませんでした
with
[
_Ty=int,
_Secure_validation=true
]
C:\Program Files\Microsoft Visual Studio 8\VC\include\xutility(1856) : 'std::operator -' の宣言を確認してください。
C:\Program Files\Microsoft Visual Studio 8\VC\include\algorithm(3112) : error C2784: 'reverse_iterator<_RanIt>::difference_type std::operator -(const std::reverse_iterator<_RanIt> &,const std::reverse_iterator<_RanIt2> &)' : テンプレート 引数を 'const std::reverse_iterator<_RanIt> &' に対して 'std::list<_Ty>::_Iterator<_Secure_validation>' から減少できませんでした
with
[
_Ty=int,
_Secure_validation=true
]
C:\Program Files\Microsoft Visual Studio 8\VC\include\xutility(1856) : 'std::operator -' の宣言を確認してください。
C:\Program Files\Microsoft Visual Studio 8\VC\include\algorithm(3112) : error C2784: 'reverse_iterator<_RanIt>::difference_type std::operator -(const std::reverse_iterator<_RanIt> &,const std::reverse_iterator<_RanIt2> &)' : テンプレート 引数を 'const std::reverse_iterator<_RanIt> &' に対して 'std::list<_Ty>::_Iterator<_Secure_validation>' から減少できませんでした
with
[
_Ty=int,
_Secure_validation=true
]
C:\Program Files\Microsoft Visual Studio 8\VC\include\xutility(1856) : 'std::operator -' の宣言を確認してください。
C:\Program Files\Microsoft Visual Studio 8\VC\include\algorithm(3112) : error C2676: 二項演算子 '-' : 'std::list<_Ty>::_Iterator<_Secure_validation>' は、この演算子または定義済の演算子に適切な型への変換の定義を行いません。(新しい動作; ヘルプを参照)
with
[
_Ty=int,
_Secure_validation=true
]
C:\Program Files\Microsoft Visual Studio 8\VC\include\algorithm(3112) : error C2780: 'void std::_Sort(_RanIt,_RanIt,_Diff,_Pr)' : 4 引数が必要です - 3 が設定されます。
C:\Program Files\Microsoft Visual Studio 8\VC\include\algorithm(3231) : 'std::_Sort' の宣言を確認してください。


コンセプトを使うとどうなるか

template <RandomAccessIterator Iterator>
void sort(Iterator first, Iterator last>
{
    ....
}

先ほどと同様のプログラムをコンパイルすると、以下のようなエラーメッセージを吐いてくれる(はず)

エラー! : 第一引数はRandomAccessIteratorの要求を満たしません


次はConcept Map

先ほど使用したRandomAccessIteratorコンセプトだが、これが以下のように定義されているとしよう

auto concept RandomAccessIterator<class T> {
    typename value_type      = T::value_type;
    typename difference_type = T::difference_type;
}

すると、以下のプログラムはコンパイルエラーになる

int ar[3];
sort(ar, ar + 3); // エラー!第一引数はRandomAccessIteratorの要求を満たしません

ポインタはRandomAccessIteratorとして使用できないと困る


そこで、concept_mapを使ってRandomAccessIteratorコンセプトを特殊化する

template <class T>
concept_map RandomAccessIterator<T*> {
    typedef T         value_type;
    typedef ptrdiff_t difference_type;
}

これでポインタをRandomAccessIteratorとして使うことができる


concept_mapはいろいろなトリックが生まれそうだ


某巨大掲示板C++0xスレッドで以下のようなものがあった

concept_map InputIterator<int> {
    typedef int value_type; 
    typedef int reference; 
    typedef int* pointer; 
    typedef int difference_type; 
    int operator*(int x) { return x; } 
}

copy(1, 17, ostream_iterator<int>(cout, " ")); 
 // prints: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

これは強力だ!




例:) 拡張for文のコンセプト

concept For<class C> {
    typename iterator;
    iterator begin(C&);
    iterator end(C&);
}

template <class T, int N>
concept_map For<T[N]> {
    typedef T* iterator;
    iterator begin(T (&ar)[N]) { return ar; }
    iterator end(T (&ar)[N])   { return ar + N; }
}

template <Container X>
concept_map For<X> {
    typedef typename X::iterator iterator;
    iterator begin(X& x) { return x.begin(); }
    iterator end(X& x)   { return x.end(); }
}

template <Container X>
concept_map For<const X> {
    typedef typename X::const_iterator iterator;
    iterator begin(X& x) { return x.begin(); }
    iterator end(X& x)   { return x.end(); }
}

STLのアルゴリズムもコンセプトに合わせて書き換えようという提案がでている

template <Container C>
void sort(C& c);

template <Container C, Predicate Cmp>
void sort(C& c, Cmp less);

template <RandomAccessIterator Iterator>
void sort(Iterator first, Iterator last);

template <RandomAccessIterator Iterator, Predicate Cmp>
void sort(Iterator first, Iterator last, Cmp less);
vector<int> v = {3, 1, 4};
sort(v);


C++0x標準のコンセプトは, で定義される(N2322)

Integral, Arithmetic, Floating, Predicate...
SameType, DerivedFrom...

type_traitsより簡単で強力だ



【総評】
C#JavaのGenericsは、「型Tはwhereで指定された型と継承関係でなければならない」というものだが
C++0xのコンセプトは「型Tはrequiresで指定された条件(xxメンバ関数を持っている, xx型を持っている)を満たさなければならない」
というものなので、使い勝手が良さそうだ


【詳細はこちら】
本の虫 - Concepts Extending C++ Templates For Generic Programming(2007/03/07)




C++0x言語拡張まとめ