前回述べたように、BCCLを用いればある型のシグネチャを制限する事ができる。
つまりある型T
のインスタンスvar
が
std::sort(var.begin(), var.end());
int N = var.size();
のような"使い方"(signature)が可能である事を明示的に要請する事ができる。
これはソートが安全に実行できる事を保証するのではなく、
.begin()
や.end()
なるメンバーを持っていて、
std::sort
の引数の型として適切な型を返す事を保証する。
つまり型演算だけを実行する。
今回はごく簡単なシグネチャを要請するコンセプトチェッククラスを作成する。
たとえばある値を参照でもらって中身を書き換える関数を考えよう。
そのものを書き換える場合(inplace)と、
元のをそのままに別のインスタンスへ変更したものを書き換える場合(outplace)
の両方が実装していてほしいとする。
T val1, val2;
f(val1); // inplace
f(val1, val2); // outplace
みたいに使えると 仮定して 、
このシグネチャを利用したコードを実装するとしよう。
この仮定をf
に要請するにはどうすればいいか?
純粋仮想関数を使用する
まずはコンセプト使わずにOOPのインターフェイスを利用する。
template <class Val> struct TwoWayFunc {
virtual void operator()(Val &) = 0;
virtual void operator()(const Val &, Val &) = 0;
};
このインターフェイスクラスをf
に継承させればいい。
確かにf
を自分が定義するならば問題ない。
しかしジェネリックなライブラリを作成する場合、
このf
は一般にライブラリの外部でライブラリのユーザーが定義する関数である。
また複数の別個のシグネチャを仮定する事ができない。
コンセプトを使用する
コンセプトチェッククラスを定義する
template <class Func, class Var> struct TwoWayFunc{
private:
Func f; // 実際にインスタンス化する必要はない
Var v; // 同上
static const Var &cv; // constの引数にする場合
public:
BOOST_CONCEPT_USAGE(TwoWayFunc) {
f(v);
f(cv, v);
}
};
これだけで十分である。
このクラスを用いて、ジェネリックなアルゴリズムを実装する際に、
template<class Func, class Var>
void generic_algorithm(){
BOOST_CONCEPT_ASSERT((TwoWayFunc<Func, Var>));
Func f(...);
Var var;
...
}
のようにBOOST_CONCEPT_ASSERT
を用いて要請すればいい。
これで上述したシグネチャが使えない場合、コンパイル時にBOOST_CONCEPT_ASSERT
の所でエラーとなる。
実際のアルゴリズムの最中でエラーとなるわでないので、
ライブラリの使用者に(ライブラリの実装ミスでなく)Func
の型が問題なのだと報せられる。