事前に変更に備えておく方が良いので、C ++ 20標準、つまりconceptに含まれるものを検討することをお勧めします。
コンセプトステータス
現在、概念には技術仕様( TS:技術仕様 )のステータスがあります: ISO / IEC TS 19217:2015を説明する文書。 このようなドキュメントは、言語標準の革新を採用する前に、これらの革新をC ++コミュニティでテストおよび調整するために必要です。 gccコンパイラは、2015年以降、実験モードで概念の技術仕様をサポートしています。
技術仕様のコンセプトと現在のドラフトC ++ 20のコンセプトは異なるが、それほど多くないことは注目に値します。 この記事では、技術仕様のバリアントについて説明します。
理論
クラスおよび関数テンプレートは制限を受ける場合があります。 制約は、テンプレート引数に要件を課します。 概念は、このような制約の名前付きセットです。 各概念は、これらの制約をテストするブール関数( 述語 )です。 検証は、概念または制限に関連付けられたテンプレートをインストールする際のコンパイル段階で実行されます。 そのようなチェックが失敗すると、コンパイラはどのテンプレート引数がどの制約のチェックに失敗したかを示します。
練習する
概念の意味と目的が理解できたので、構文を検討できます。 概念の定義には、変数と関数の2つの形式があります。 変数の形式に興味があります。 通常のテンプレート変数の定義に非常に似ていますが、 conceptキーワードがあります。
template<typename T> concept bool MyConcept = /* ... */;
コメントの代わりに、boolにキャストされるconstexpr式を記述する必要があります。 この式は、テンプレート引数の制限です。 テンプレートを概念に限定するには、typename(またはclass)の代わりにその名前を使用する必要があります。
たとえば、整数の場合:
// ( // ) #include <type_traits> template<typename T> // concept bool MyIntegral = std::is_integral<T>::value; //template <typename T> template<MyIntegral T> bool compare (T a, T b) { return a < b; } void foo () { compare (123u, 321u); /// OK compare (1.0, 2.0); /** : MyIntegral (std::is_integral<double>::value = false) */ }
要件式を使用して、より複雑な制約を設定できます。 要件式は、 整形式、式の戻り値、型の存在を検証できます。 構文はここで十分に解析されます 。
#include <unordered_set> #include <vector> template<typename T> concept bool MyComparable = requires (T a, T b) { a < b; /// , { a < b } -> bool; /// , bool }; template<MyComparable T> bool compare (T a, T b) { return a < b; } void foo () { std::vector<int> vecA = {1, 2, 3}, vecB = {1, 2, 4}; std::unordered_set<int> setA = {1, 2, 3}, setB = {1, 2, 4}; compare (vecA, vecB); /// OK compare (setA, setB); /** MyComparable std::unordered_set . ( a < b ) . */ }
仕分け
並べ替えを書く際に概念はどのように役立ちますか アルゴリズム自体は変更されませんが、概念を使用してソートパターンを改善できます。 この例を考えてみましょう:
#include <algorithm> struct NonComparable {}; int main () { std::vector<NonComparable> vector = {{}, {}, {}, {}, {}, {}, {}, {}}; std::sort (vector.begin(), vector.end()); // }
エラーは、NonComparable構造に比較演算がないことです。 コンパイラエラーがどのように見えるか想像できますか? そうでない場合は、スポイラーの下を見てください。
[username@localhost concepts]$ g++ -std=c++17 main.cpp In file included from /opt/rh/devtoolset-7/root/usr/include/c++/7/bits/stl_algobase.h:71:0, from /opt/rh/devtoolset-7/root/usr/include/c++/7/vector:60, from main.cpp:1: /opt/rh/devtoolset-7/root/usr/include/c++/7/bits/predefined_ops.h: In instantiation of 'constexpr bool __gnu_cxx::__ops::_Iter_less_iter::operator()(_Iterator1, _Iterator2) const [with _Iterator1 = __gnu_cxx::__normal_iterator<NonComparable*, std::vector<NonComparable> >; _Iterator2 = __gnu_cxx::__normal_iterator<NonComparable*, std::vector<NonComparable> >]': /opt/rh/devtoolset-7/root/usr/include/c++/7/bits/stl_algo.h:81:17: required from 'void std::__move_median_to_first(_Iterator, _Iterator, _Iterator, _Iterator, _Compare) [with _Iterator = __gnu_cxx::__normal_iterator<NonComparable*, std::vector<NonComparable> >; _Compare = __gnu_cxx::__ops::_Iter_less_iter]' /opt/rh/devtoolset-7/root/usr/include/c++/7/bits/stl_algo.h:1921:34: required from '_RandomAccessIterator std::__unguarded_partition_pivot(_RandomAccessIterator, _RandomAccessIterator, _Compare) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<NonComparable*, std::vector<NonComparable> >; _Compare = __gnu_cxx::__ops::_Iter_less_iter]' /opt/rh/devtoolset-7/root/usr/include/c++/7/bits/stl_algo.h:1953:38: required from 'void std::__introsort_loop(_RandomAccessIterator, _RandomAccessIterator, _Size, _Compare) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<NonComparable*, std::vector<NonComparable> >; _Size = long int; _Compare = __gnu_cxx::__ops::_Iter_less_iter]' /opt/rh/devtoolset-7/root/usr/include/c++/7/bits/stl_algo.h:1968:25: required from 'void std::__sort(_RandomAccessIterator, _RandomAccessIterator, _Compare) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<NonComparable*, std::vector<NonComparable> >; _Compare = __gnu_cxx::__ops::_Iter_less_iter]' /opt/rh/devtoolset-7/root/usr/include/c++/7/bits/stl_algo.h:4836:18: required from 'void std::sort(_RAIter, _RAIter) [with _RAIter = __gnu_cxx::__normal_iterator<NonComparable*, std::vector<NonComparable> >]' main.cpp:6:44: required from here /opt/rh/devtoolset-7/root/usr/include/c++/7/bits/predefined_ops.h:43:23: error: no match for 'operator<' (operand types are 'NonComparable' and 'NonComparable') { return *__it1 < *__it2; } ~~~~~~~^~~~~~~~ In file included from /opt/rh/devtoolset-7/root/usr/include/c++/7/bits/stl_algobase.h:67:0, from /opt/rh/devtoolset-7/root/usr/include/c++/7/vector:60, from main.cpp:1: /opt/rh/devtoolset-7/root/usr/include/c++/7/bits/stl_iterator.h:888:5: note: candidate: template<class _IteratorL, class _IteratorR, class _Container> bool __gnu_cxx::operator<(const __gnu_cxx::__normal_iterator<_IteratorL, _Container>&, const __gnu_cxx::__normal_iterator<_IteratorR, _Container>&) operator<(const __normal_iterator<_IteratorL, _Container>& __lhs, ^~~~~~~~ /opt/rh/devtoolset-7/root/usr/include/c++/7/bits/stl_iterator.h:888:5: note: template argument deduction/substitution failed: In file included from /opt/rh/devtoolset-7/root/usr/include/c++/7/bits/stl_algobase.h:71:0, from /opt/rh/devtoolset-7/root/usr/include/c++/7/vector:60, from main.cpp:1: /opt/rh/devtoolset-7/root/usr/include/c++/7/bits/predefined_ops.h:43:23: note: 'NonComparable' is not derived from 'const __gnu_cxx::__normal_iterator<_IteratorL, _Container>' { return *__it1 < *__it2; } ~~~~~~~^~~~~~~~ ..
コード内のこのような小さなエラー、およびコンパイラー内の非常に大きなエラー。 アビドニー、はい!?
このようなエラーは概念の助けを借りて減らすことができます。そのため、それらを使用してラッパーを作成します。 ソートにはイテレータが必要なので、ソート可能なイテレータの概念を記述する必要があります。 このようなイテレーターには、いくつかの小さなコンセプトが必要です。 たとえば、比較可能なオブジェクト(上記)、交換されたオブジェクト:
template<typename T> concept bool MySwappable = requires (T a, T b) { std::swap(a, b); // };
ローミングオブジェクト
template<typename T> concept bool MyMovable = requires (T a) { T (std::move(a)); // a = std::move(a); // };
ランダムアクセス反復子
template<typename T> concept bool MyRAIterator = requires (T it) { typename T::value_type; // it++; // Random Access it--; it += 2; it -= 2; it = it + 2; it = it - 2; { *it } -> typename T::value_type; // };
すべての単純な概念の準備ができたら、Sortableイテレータの複合概念を定義できます。
template<typename T> concept bool MySortableIterator = MyRAIterator<T> && // MyMovable<typename T::value_type> && // MyComparable<typename T::value_type> && // MySwappable<typename T::value_type>; //
それを使用して、ラッパーが記述されます。
template<MySortableIterator T> void conceptualSort (T begin, T end) { std::sort (begin, end); }
比類のないオブジェクトで「概念」ソートを呼び出すと、
struct NonComparable {}; int main () { std::vector<NonComparable> vector = {{}, {}, {}, {}, {}, {}, {}, {}}; conceptualSort (vector.begin(), vector.end()); // }
コンパイルエラーは16行しかかかりません。
[markgrin@localhost concepts]$ g++ -std=c++17 -fconcepts main.cpp main.cpp: In function 'int main()': main.cpp:49:49: error: cannot call function 'void conceptualSort(T, T) [with T = __gnu_cxx::__normal_iterator<NonComparable*, std::vector<NonComparable> >]' conceptualSort (vector.begin(), vector.end()); ^ main.cpp:41:6: note: constraints not satisfied void conceptualSort (T begin, T end) { ^~~~~~~~~~~~~~ main.cpp:36:14: note: within 'template<class T> concept const bool MySortableIterator<T> [with T = __gnu_cxx::__normal_iterator<NonComparable*, std::vector<NonComparable> >]' concept bool MySortableIterator = MyRAIterator<T> && MyMovable<typename T::value_type> && ^~~~~~~~~~~~~~~~~~ main.cpp:12:14: note: within 'template<class T> concept const bool MyComparable<T> [with T = NonComparable]' concept bool MyComparable = requires (T a, T b) { ^~~~~~~~~~~~ main.cpp:12:14: note: with 'NonComparable a' main.cpp:12:14: note: with 'NonComparable b' main.cpp:12:14: note: the required expression '(a < b)' would be ill-formed
もちろん、最初はエラーの内容を理解するのはまだそれほど簡単ではありませんが、いくつかの「概念的な」エラーの後、数秒で読み始められます。
おわりに
もちろん、 エラーの長さを減らすことがイノベーションの唯一の利点ではありません。 テンプレートは制限により安全です。 名前付きの概念のおかげで、コードはより読みやすくなります(最も頻繁に使用されるものはライブラリに含まれます )。 一般的に、C ++はその機能(テンプレート)部分で拡張されます。