C++20のレンジについて調べたことの覚え書きです。
ここで扱うレンジ
ここでは<ranges>
ヘッダーで定義されるstd::ranges::range<T>
コンセプトを満たす型をレンジとして扱います。range<T>
の要件は型T
の変数t
に対してstd::ranges::begin(t)
とstd::ranges::end(t)
が定義されることだけです。
range
に要件を加えたコンセプトとしてcommon_range
やforward_range
等が定義されています。
型がレンジか確認する。
std::ranges::range<T>
コンセプトは型が要件を満たすならtrue
、満たさないならfalse
を返します。次のコードはint[10]
型がレンジか、どのようなレンジかを確認しています。output_range
は出力型も必要とするため、OutT
としてint
を指定しています。
#include <iostream> #include <ranges> template <typename T, typename OutT> void print_range_concept_results(); int main() { std::cout.setf(std::cout.boolalpha); print_range_concept_results<int[], int>(); // using T = int[10] // range<T>: true // common_range<T>: true // borrowed_range<T>: false // input_range<T>: true // output_range<T, OutT>: true // forward_range<T>: true // bidirectional_range<T>: true // random_access_range<T>: true // contiguous_range<T>: true return 0; } template <typename T, typename OutT> void print_range_concept_results() { std::cout << "using T = " << typeid(T).name() << std::endl; std::cout << "range<T>: " << std::ranges::range<T> << std::endl; std::cout << "common_range<T>: " << std::ranges::common_range<T> << std::endl; std::cout << "borrowed_range<T>: " << std::ranges::borrowed_range<T> << std::endl; std::cout << "input_range<T>: " << std::ranges::input_range<T> << std::endl; std::cout << "output_range<T, OutT>: " << std::ranges::output_range<T, OutT> << std::endl; std::cout << "forward_range<T>: " << std::ranges::forward_range<T> << std::endl; std::cout << "bidirectional_range<T>: " << std::ranges::bidirectional_range<T> << std::endl; std::cout << "random_access_range<T>: " << std::ranges::random_access_range<T> << std::endl; std::cout << "contiguous_range<T>: " << std::ranges::contiguous_range<T> << std::endl; }
int
型の配列とSTLコンテナのレンジ該当状況
以下の表ではrange以降の型名からサフィックス_range
を省略しています。例えばcommonはcommon_range
を指します。
T | OutT | range | common | borrowed | input | output | forward | bidirectional | random_access | contigunous |
---|---|---|---|---|---|---|---|---|---|---|
int [0 ] |
int | |||||||||
int [10] |
int | ○ | ○ | ○ | ○ | ○ | ○ | ○ | ○ | |
array<int,0> |
int | ○ | ○ | ○ | ○ | ○ | ○ | ○ | ○ | |
array<int,10> |
int | ○ | ○ | ○ | ○ | ○ | ○ | ○ | ○ | |
initializer_list<int> |
int | ○ | ○ | ○ | ○ | ○ | ○ | ○ | ||
vector<int> |
int | ○ | ○ | ○ | ○ | ○ | ○ | ○ | ○ | |
list<int> |
int | ○ | ○ | ○ | ○ | ○ | ○ | |||
span<int,-1> |
int | ○ | ○ | ○ | ○ | ○ | ○ | ○ | ○ | ○ |
map<int,int> |
int | ○ | ○ | ○ | ○ | ○ | ||||
queue<int> |
int | |||||||||
stack<int> |
int | |||||||||
unique_ptr<int> |
int | |||||||||
unique_ptr<int [10]> |
int |
確認用コード
Markdownの表を出力するコードです。typeid(...).name
は適当に修正します。
#include <iostream> #include <ranges> template <typename T, typename OutT> void print_range_concept_results(); struct custom_boolstr : std::numpunct<char> { std::string do_truename() const { return "○"; } std::string do_falsename() const { return ""; } }; #include <array> #include <list> #include <map> #include <memory> #include <stack> #include <queue> #include <vector> int main() { std::cout.setf(std::cout.boolalpha); std::cout.imbue(std::locale(std::cout.getloc(), new custom_boolstr())); print_range_concept_results<int[], int>(); print_range_concept_results<int[10], int>(); print_range_concept_results<std::array<int, 0>, int>(); print_range_concept_results<std::array<int, 10>, int>(); print_range_concept_results<std::initializer_list<int>, int>(); print_range_concept_results<std::vector<int>, int>(); print_range_concept_results<std::list<int>, int>(); print_range_concept_results<std::span<int>, int>(); print_range_concept_results<std::map<int, int>, int>(); print_range_concept_results<std::queue<int>, int>(); print_range_concept_results<std::stack<int>, int>(); print_range_concept_results<std::unique_ptr<int>, int>(); print_range_concept_results<std::unique_ptr<int[10]>, int>(); return 0; } template <typename T, typename OutT> void print_range_concept_results() { std::cout << "|" << typeid(T).name() << "|" << typeid(OutT).name() << "|" << std::ranges::range<T> << "|" << std::ranges::common_range<T> << "|" << std::ranges::borrowed_range<T> << "|" << std::ranges::input_range<T> << "|" << std::ranges::output_range<T, OutT> << "|" << std::ranges::forward_range<T> << "|" << std::ranges::bidirectional_range<T> << "|" << std::ranges::random_access_range<T> << "|" << std::ranges::contiguous_range<T> << "|" << std::endl; }