次の方法で共有


<ranges> ビュー クラス

view は、(owning_viewを除く) 所有していない要素を参照する軽量の範囲です。 ビューは通常、別の範囲に基づいており、変換またはフィルター処理によって、ビューを見る別の方法を提供します。 たとえば、 std::views::filter は、指定した条件を使用して別の範囲から要素を選択するビューです。

ビュー内の要素にアクセスすると、"遅延" が行われ、要素を取得したときにのみ作業が行われます。 これにより、パフォーマンスが低下することなく、ビューを結合 () できます。

たとえば、範囲の偶数要素のみを提供するビューを作成し、それらを 2 乗して変換することができます。 フィルター処理と変換を行う作業は、アクセスする要素に対してのみ実行され、アクセスした場合にのみ実行されます。

ビューは、含まれる要素の数に関係なく、一定の時間にコピー、割り当て、破棄できます。 これは、ビューが参照する要素を所有していないため、コピーを作成する必要がないためです。 このため、パフォーマンスの低下なしにビューを作成できます。

通常は、 範囲アダプターを使用してビューを作成。 範囲アダプターはビューを作成するための目的の方法であり、ビュー クラスを直接インスタンス化するよりも使いやすく、ビュー クラスを直接インスタンス化するよりも効率的な場合があります。 ビュー クラスは、既存のビューの種類に基づいて独自のカスタム ビューの種類を作成する必要がある場合に直接公開されます。

ベクトルで 3 つ割り切れる要素の四角形のビューを作成する簡単な例を次に示します。

// requires /std:c++20 or later
#include <ranges>
#include <vector>
#include <iostream>

int main()
{
    std::vector<int> input =  {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    auto divisible_by_three = [](const int n) {return n % 3 == 0;};
    auto square = [](const int n) {return n * n;};

    auto x = input
             | std::views::filter(divisible_by_three)
             | std::views::transform(square);

    for (int i : x)
    {
        std::cout << i << ' '; // 0 9 36 81
    }
}
0 9 36 81

基になっている範囲が変更された後にビューを使用すると、未定義の動作が発生する可能性があります。 たとえば、基になるベクターに基づく要素を追加または削除する場合、ベクターに基づく reverse_view は再利用しないでください。 基になるベクターを変更すると、コンテナーの end 反復子が無効になります。これには、ビューが作成した反復子のコピーが含まれます。

ビューは簡単に作成できるため、基になる範囲を変更する場合は通常、ビューを再作成する必要があります。 次の例では、ビュー パイプラインを変数に格納して再利用できるようにする方法を示します。

// requires /std:c++20, or later
#include <iostream>
#include <ranges>
#include <vector>
#include <list>
#include <string_view>
#include <algorithm>

template<typename rangeType>
void show(std::string_view msg, rangeType r)
{
    std::cout << msg;
    std::ranges::for_each(r,
        [](auto e)
        {
            std::cout << e << ' ';
        });
    std::cout << '\n';
}

int main()
{
    std::vector v{ 1, 2, 3, 4 };
    show("v: ", v);

    // You can save a view pipeline
    auto rev3 = std::views::take(3) | std::views::reverse;

    show("v | rev3: ", v | rev3); // 3 2 1

    v.insert(v.begin(), 0); // v = 0 1 2 3 4
    show("v: ", v);

    // Because modifying the vector invalidates its iterators, rebuild the view.
    // We are reusing the view pipeline we saved earlier
    show("v | rev3(v): ", rev3(v));
}
v: 1 2 3 4
v | rev3: 3 2 1
v: 0 1 2 3 4
v | rev3(v): 2 1 0

std::ranges名前空間では、次のビュー クラスが定義されています。

表示 説明
basic_istream_viewC++20 入力ストリームからの連続する要素のビュー。 特殊化には、 istream_viewwistream_viewが含まれます。
common_viewC++20 反復子/sentinel 型が異なるビューを、同じ反復子/センチネル型のビューに適合させます。
drop_viewC++20 別のビューから作成され、最初の count 要素をスキップします。
drop_while_viewC++20 述語が保持されている限り、先頭の要素をスキップして、別のビューから作成されます。
elements_viewC++20 コレクション内の各タプルに似た値への、選択したインデックスのビュー。 たとえば、 std::tuple<string, int> 値の範囲を指定すると、各タプルのすべての string 要素で構成されるビューが作成されます。
empty_viewC++20 要素のないビュー。
filter_viewC++20 述語と一致しない範囲の要素を除外します。
iota_viewC++20 増分値のシーケンスを含む生成されたビュー。
join_viewC++20 複数の範囲のすべての要素を 1 つのビューに結合します。
keys_viewC++20 コレクション内の各タプルに似た値への最初のインデックスのビュー。 たとえば、 std::tuple<string, int> 値の範囲を指定すると、各タプルの string 要素で構成されるビューが作成されます。
lazy_split_viewC++20 区切り記号に基づいてビューをサブ範囲に分割します。
owning_viewC++20 別の範囲から要素の所有権を取得します。
ref_viewC++20 別の範囲に属する要素を参照するビュー。
reverse_viewC++20 範囲の要素を逆の順序で表示します。
single_viewC++20 1 つの要素のみを含むビュー。
split_viewC++20 区切り記号に基づいてビューをサブ範囲に分割します。
subrangeC++20 範囲の要素の一部のビュー 。開始反復子とセンチネルによって定義されます。
take_viewC++20 指定した範囲の先頭から取得された要素の数を格納します。
take_while_viewC++20 指定された述語に一致する範囲の先頭要素を格納します。
transform_viewC++20 変換関数が各要素に適用された後の基になるシーケンスのビュー。
values_viewC++20 コレクション内の各タプルに似た値への 2 番目のインデックスのビュー。 たとえば、 std::tuple<string, int> 値の範囲を指定すると、各タプルの int 要素で構成されるビューが作成されます。

これらのクラスの多くは、対応する range アダプター それらのインスタンスを作成する std:views 名前空間にあります。 ビュー クラスを直接作成する代わりに、アダプターを使用してビューを作成することをお選びください。 範囲アダプターは、ビューを作成するための目的の方法であり、使いやすく、場合によってはより効率的です。

クラスの特性を表示する

各ビュー クラス トピックには、構文セクションの後に Characteristics セクションがあります。 Characteristics セクションには、次のエントリがあります。

  • 範囲アダプター: ビューを作成する範囲アダプターへのリンク。 通常は、ビュー クラスを直接作成するのではなく、範囲アダプターを使用してビューを作成するため、便宜上ここに記載されています。

  • 基になる範囲: ビューには、使用できる基になる範囲の種類に対して異なる反復子要件があります。 反復子 種類の詳細については 反復子階層を参照してください。

  • ビュー反復子カテゴリ: ビューの反復子カテゴリ。 ビューが範囲を調整する場合、ビューの反復子型は通常、基になる範囲の反復子型と同じです。 ただし、一部のビューでは異なる場合があります。 たとえば、基になる範囲にrandom_access_iteratorがある場合でも、reverse_viewにはbidirectional_iteratorがあります。

  • 要素の型: ビューの反復子が返す要素の型。

  • サイズ: ビューが参照する要素の数を返すことができるかどうか。 すべてのビューで実行できるわけではありません。

  • 共通範囲: ビューが common_rangeかどうかを指定します。これは、開始反復子と sentinel の型が同じであることを意味します。 一般的な範囲は、反復子のペアで動作する事前範囲コードに役立ちます。 たとえば、シーケンス コンテナーの反復子ペア コンストラクター ( vector(ranges::begin(x), ranges::end(x))など) です。

  • 借用範囲: ビューが借用範囲かどうかを指定します。 borrowed_range<T>は、Tが破棄された後にTに反復子を使用できることを意味します。

    コンテナーを破棄すると要素が解放され、反復子が無効になるため、標準コンテナーは借用範囲ではありません。 その場合、反復子は破棄後に "未解決" のままであると言います。

    たとえば、 std::ranges::find() は通常、範囲引数で見つかった要素を指す反復子を返します。 範囲引数が一時 (右辺値) コンテナーの場合は、返された反復子を格納し、後で使用するのは間違いです。これは "未解決" であるためです。

    反復子 (またはサブ範囲) を返す範囲アルゴリズムは、引数が左辺値 (非一時的) または借用範囲である場合にのみ行われます。 それ以外の場合は、 std::dangling オブジェクトが返されます。これは、反復子のように使用しようとした場合の問題に関するエラー メッセージのヒントを提供します。

  • const反復可能です: ビューのconst インスタンスを反復処理できるかどうかを示します。 すべての const ビューを反復処理できるわけではありません。 ビュー const 反復できない場合は、 for (const auto& element : as_const(theView)) を反復処理したり、ビューへの const 参照を受け取って反復処理を試みる関数に渡したりすることはできません。

範囲反復子階層

各ビュー クラス トピックの Characteristics セクションでは、 基になる範囲の反復子カテゴリ および View 反復子カテゴリ 範囲/ビューがサポートする反復子の種類を参照します。 範囲反復子には、C++20 の概念で識別される 6 つのカテゴリがあります。 範囲反復子の階層は、機能の増加順に次のとおりです。

範囲反復子の概念 説明
output_range 書き込み専用で、前に進むだけです。単一パス。
input_range 読み取り専用、前方にのみ移動します。単一パス。
forward_range 前方にのみ移動します。マルチパス。
bidirectional_range 前後に移動することができます。マルチパス。
random_access_range インデックスを使用してコレクションにアクセスできます。マルチパス。
contiguous_range インデックスを使用してコレクションにアクセスでき、要素は連続してメモリに格納されます。

一般に、反復子には、テーブル内の反復子の前にある反復子の機能があります。 たとえば、 bidirectional_range には forward_rangeの機能がありますが、その逆の機能はありません。 input_rangeに書き込めないため、output_rangeの機能を持たないinput_rangeを除きます。

ステートメント "requires input_range 以上" は、ビューが input_rangeforward_rangebidirectional_rangerandom_access_range、または contiguous_range 反復子と共に使用できることを意味 input_range

範囲反復子階層は、反復子階層に直接関連付けられます。 詳細については、「 Iterator の概念を参照してください。

関連項目

<ranges>
範囲アダプター