(誤解を恐れずに言うなら)conceptはテンプレートで渡す際のclass TのTに条件を付けるための機能。
以下のように、
という形で定義する。
型Tで与えられた変数valの使い方を定義すると考えると読みやすくなる。
#include <iostream>
// T型の変数valは、print()と++を持たなければならない
template <class T> concept CallableFunction = requires (T val) { val.print(); // print()関数があることを要求 val.operator++(); // ++演算子があることを要求 };
// CallableFunctionの制約がついている所で使用できるクラス class MyValidClass { int val; public: MyValidClass() { val = 5; } void print() {// print()関数を定義 std::cout << "val == " << val << std::endl; } MyValidClass& operator++() {// ++演算子を要求 ++val; return *this; } };
// CallableFunctionの制約がある場合は指定できない。 // operator++() がない。 class MyInvalid { int val; public: MyInvalid() { val = 5; } void print() { std::cout << "val == " << val << std::endl; } };
// print()関数と++演算子を持つクラスを引数に取る関数 template <CallableFunction Type> void func(Type val) { val.print(); }
int main() { MyValidClass my; func(my); }
{ } 内に式を書くと、その式を書くことができるような型だけを要求することができる。
#include <iostream> #include <algorithm> #include <vector>
template <class T> concept Sortable = requires (T& val) { // { } 内の式が有効であることを要求する書き方ができる。 // 例えば、以下は T 型の val はソート可能であることを要求する { std::sort(val.begin(), val.end()) }; };
// print()関数と++演算子を持つクラスを引数に取る関数 template <Sortable Type> void func(Type& val) { std::sort(val.begin(), val.end()); }
int main() { std::vector<int> my{ 5,3,8 }; func(my); for (auto& i : my) { std::cout << i << std::endl; } }
{ 式 } で、式が有効であることを要求できるが、式の結果のデータ型を要求するには
とする。
この仕組みを応用すれば、関数の戻り値を要求できる。
#include <iostream> #include <algorithm> #include <vector>
template <class T> concept Sizable = requires (T & val) { { val.size() // size()関数を持つ }->std::same_as<size_t>; // size()関数の戻り値はsize_t型 // std::same_asはconcept。式の戻り値の型を指定する。 };
class MyArray { std::vector<int>vec; public: void push_back(int i) { vec.push_back(i); } int size() { return vec.size(); } };
// この関数は、戻り値がsize_t型のsize()関数を持つ型しか受け付けない template <Sizable Type> void func(Type& val) { std::cout<<val.size()<<std::endl; }
int main() { std::vector<int> my; // MyArray my; my.push_back(3); my.push_back(1); my.push_back(8); func(my); }