potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。はてなブログ無料版なので記事の上の方はたぶん広告です。記事中にも広告挿入されるみたいです。

C++20 レンジについて調べたこと

C++20のレンジについて調べたことの覚え書きです。

ここで扱うレンジ

ここでは<ranges>ヘッダーで定義されるstd::ranges::range<T>コンセプトを満たす型をレンジとして扱います。range<T>の要件は型Tの変数tに対してstd::ranges::begin(t)std::ranges::end(t)が定義されることだけです。

rangeに要件を加えたコンセプトとしてcommon_rangeforward_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;
}