<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_view C++20 |
入力ストリームからの連続する要素のビュー。 特殊化には、 istream_view と wistream_view が含まれます。 |
common_view C++20 |
反復子/sentinel 型が異なるビューを、同じ反復子/センチネル型のビューに適合させます。 |
drop_view C++20 |
別のビューから作成され、最初の count 要素をスキップします。 |
drop_while_view C++20 |
述語が保持されている限り、先頭の要素をスキップして、別のビューから作成されます。 |
elements_view C++20 |
コレクション内の各タプルに似た値への、選択したインデックスのビュー。 たとえば、 std::tuple<string, int> 値の範囲を指定すると、各タプルのすべての string 要素で構成されるビューが作成されます。 |
empty_view C++20 |
要素のないビュー。 |
filter_view C++20 |
述語と一致しない範囲の要素を除外します。 |
iota_view C++20 |
増分値のシーケンスを含む生成されたビュー。 |
join_view C++20 |
複数の範囲のすべての要素を 1 つのビューに結合します。 |
keys_view C++20 |
コレクション内の各タプルに似た値への最初のインデックスのビュー。 たとえば、 std::tuple<string, int> 値の範囲を指定すると、各タプルの string 要素で構成されるビューが作成されます。 |
lazy_split_view C++20 |
区切り記号に基づいてビューをサブ範囲に分割します。 |
owning_view C++20 |
別の範囲から要素の所有権を取得します。 |
ref_view C++20 |
別の範囲に属する要素を参照するビュー。 |
reverse_view C++20 |
範囲の要素を逆の順序で表示します。 |
single_view C++20 |
1 つの要素のみを含むビュー。 |
split_view C++20 |
区切り記号に基づいてビューをサブ範囲に分割します。 |
subrange C++20 |
範囲の要素の一部のビュー 。開始反復子とセンチネルによって定義されます。 |
take_view C++20 |
指定した範囲の先頭から取得された要素の数を格納します。 |
take_while_view C++20 |
指定された述語に一致する範囲の先頭要素を格納します。 |
transform_view C++20 |
変換関数が各要素に適用された後の基になるシーケンスのビュー。 |
values_view C++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_range
、 forward_range
、 bidirectional_range
、 random_access_range
、または contiguous_range
反復子と共に使用できることを意味 input_range
。
範囲反復子階層は、反復子階層に直接関連付けられます。 詳細については、「 Iterator の概念を参照してください。