制約について
C++20で追加予定の機能。
template
のエラー文が長い上に分かりづらいという問題点を解消すべく追加された。(と勝手に思っている)
使い方
制約は以下の3通りの記述ができる。
// (1)
template<typename T>
requires /* bool値(コンパイル時定数式) */
void f(T);
// (2)
template<typename T>
void f(T)
requires /* bool値(コンパイル時定数式) */;
// (3)
template</* concept */ T>
void f(T);
(3)の書き方がシンプルでわかりやすいが、制約を追加したい場合にconceptを変更するか(1)や(2)の記述に変更する必要があるため、拡張性で劣る。
/* bool値(コンパイル時定数式) */
とあるがここには主にrequires式やconceptが入る。
requires式について
柔軟な制約を記述する上で必ず必要になる式。bool値を返すコンパイル時定数式でだいたいあっているとおもう。
以下の2通りの書き方がある。
// (1)
requires {
// 式
}
// (2)
requires (パラメータ){
// 式
}
(1)に関しては完全にただのコンパイル時定数式らしいので以下のコードがエラー無しで通る。
std::cout << (requires{std::vector<int>(10);}) << std::endl; // output 1
制約には使用するであろう式をそのまま書く。
例えばT型に対するstd::make_unique<T>()
を実行したい場合は
requires {
std::make_unique<T>();
}
といった感じ。
T型とU型の変数に対して実行したい式があった場合はパラメータを使用する。
requires (T t, U u) {
++u; // 前置++演算が有効
t[0]>(*u); // t[0]と(*u)が有効かつ戻り値が比較可能
{t.begin()} -> std::same_as<U>; // 複合要件 t.begin()が実行可能かつ戻り値の型がU
}
複合要件はnoexcept
や戻り値の型を指定する場合の記述方法。
{式} noexcept(任意) -> concept(任意)
の記述らしい(戻り値の方にconcept以外が指定できるかは不明。conceptの第一テンプレート引数が式の戻り値の型に置き換わる)。
複数記述した場合はand条件になるため、or条件は式を分ける必要がある。
conceptについて
制約用にカスタマイズされた変数テンプレートでだいたいあっていると思う。
template<typename T>
concept = /* bool値(コンパイル時定数式) */;
templateのtypenameやclassに置き換わって使用できたり、複合要件の戻り値の型要件に使用できたりする。
でも結局ただの変数テンプレートなので以下が普通に通る。
std::cout << (std::same_as<int,int>) << std::endl; // output 1
まとめ
なんの制約もなく使用できるtemplateはほぼ皆無なので、これまでのtemplateが使用されていたところには必ず制約が追加される(する必要がある)と思っていい。
type_traits
の機能の多くがconcepts
に移動すると思うんだけどtype_traits
で何が出来たかよく覚えていないのでconcepts
になにがあるかだけ気にしようと思う。