次の関数テンプレートは、複数の数値が昇順になっているかどうかを判定します。
0 1 2 3 4 5 6 7 8 9 10 11 12 |
template <typename T> bool values_in_order(const T& value1, const T& value2) { return !(value2 < value1); } template <typename T> bool values_in_order(const T& value1, const T& value2, const T& value3) { return !(value2 < value1) && !(value3 < value2); } |
大小比較を素直に<=演算子を使うのではなく、<演算子と!演算子の組み合わせにしているのは、<だけを多重定義している型もあり得るためです。<演算子はコンテナや各種アルゴリズムで使用するために必要ですので、どれか一つの関係演算子を使うとすれば、間違いなく<演算子ということになります。
こんな風に必要な数だけ引数の数が異なる関数を多重定義してもいいのですが、C++11以降では可変引数テンプレートが使えるようになっています。可変引数テンプレートを使って書くと次のようになります。
0 1 2 3 4 5 6 7 8 9 10 11 12 |
template <typename T> bool values_in_order(const T& value1, const T& value2) { return !(value2 < value1); } template <typename T, typename... Args> bool values_in_order(const T& value1, const T& value2, const Args&... args) { return values_in_order(value1, value2) && values_in_order(value2, args...); } |
引数2個の関数は先ほどと同じです。引数が3つ以上になると可変引数テンプレート版の関数が使われることになります。
可変引数テンプレートとは別の方法も考えてみることにします。STLでもよく使われる範囲を2つの反復子で指定する方法です。
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
template <typename InputIterator> bool values_in_order(InputIterator first, InputIterator last) { if (first == last) return false; auto lhs = *first++; while (first != last) { auto rhs = *first++; if (rhs < lhs) return false; lhs = rhs; } return true; } |
これであれば、vectorなどのコンテナでも配列でも、任意の列が昇順になっているかどうかを調べることができます。この関数の中でautoを使っていますので、コンパイルするにはC++11以降が必要です。C++11より前のバージョンを使うときは、autoの代わりに
0 1 2 |
typename iterator_traits<InputIterator>::value_type |
と書けばOKです(長いですね)。
なお、この関数では、先ほどの可変引数テンプレート版のように直接実引数で値を並べて使うことができません。そこで次のようにinitializer_listを引数に取る関数を多重定義することにします。
0 1 2 3 4 5 6 |
template <typename T> bool values_in_order(initializer_list<T> il) { return values_in_order(il.begin(), il.end()); } |
これで、values_in_order({ 1, 2, 3, 4 });のように呼び出せますので、可変引数テンプレート版とほぼ変わらない使い勝手が得られると思います。
今回も名前空間のstd::は省略していますのでご注意ください。