分享方式:


<ranges> 檢視類別

檢視是一個輕量型範圍,參考它不擁有的專案(但除外owning_view)。 檢視通常以另一個範圍為基礎,並透過轉換或篩選它,提供不同的檢視方式。 例如, std::views::filter 是一種檢視,使用您指定的準則,從另一個範圍選取元素。

當您存取檢視中的元素時,它會「懶散」完成,因此只有在您取得專案時才會完成工作。 這可讓您合併或 撰寫檢視,而不會降低效能。

例如,您可以建立只提供範圍中偶數元素的檢視,然後藉由將專案四等來轉換。 執行篩選和轉換的工作只會針對您存取的元素完成,而且只有在您存取它們時才完成。

無論檢視包含多少個元素,都可以在常數時間複製、指派和終結檢視。 這是因為檢視沒有其參考的專案,因此不需要製作複本。 這就是為什麼您可以撰寫檢視,而不會產生效能損失。

您通常會使用 範圍配接器來建立檢視。 範圍配接器是建立檢視的預定方式,比直接具現化檢視類別更容易使用,有時比直接具現化檢視類別更有效率。 如果您需要根據現有的檢視類型建立自己的自定義檢視類型,檢視類別會直接公開。

以下是一個簡短的範例,說明如何建立向量中三個可分割之元素平方的檢視:

// 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 類型的檢視調整為具有相同反覆運算器/sentinel 類型的檢視。
drop_viewC++20 從另一個檢視建立,略過第一個 count 元素。
drop_while_viewC++20 從另一個檢視建立,只要述詞保留,就會略過前置元素。
elements_viewC++20 檢視所選取索引到集合中每個類似 Tuple 的值。 例如,假設有一系列 std::tuple<string, int> 值,請建立包含每個 Tuple 中所有 string 元素的檢視。
empty_viewC++20 沒有元素的檢視。
filter_viewC++20 篩選出不符合述詞的範圍元素。
iota_viewC++20 產生的檢視,其中包含遞增值的序列。
join_viewC++20 將多個範圍的所有元素合併成單一檢視。
keys_viewC++20 檢視集合中每個類似 Tuple 的值的第一個索引。 例如,假設有一個值範圍 std::tuple<string, int> ,請建立檢視,其中包含每個 Tuple 中的 string 專案。
lazy_split_viewC++20 根據分隔符將檢視分割成子範圍。
owning_viewC++20 從另一個範圍取得項目的擁有權。
ref_viewC++20 參考屬於另一個範圍之項目的檢視。
reverse_viewC++20 以反向順序呈現範圍的專案。
single_viewC++20 只包含一個項目的檢視。
split_viewC++20 根據分隔符將檢視分割成子範圍。
subrangeC++20 範圍元素的檢視,如開始反覆運算器和 sentinel 所定義。
take_viewC++20 包含從範圍前面擷取的指定項目數目。
take_while_viewC++20 包含符合指定述詞之範圍的前置元素。
transform_viewC++20 轉換函式套用至每個元素之後,基礎序列的檢視。
values_viewC++20 檢視集合中每個類似 Tuple 的值的第二個索引。 例如,假設有一個值範圍 std::tuple<string, int> ,請建立檢視,其中包含每個 Tuple 中的 int 專案。

其中許多類別在命名空間中有std:views對應的範圍配接器,可建立它們的實例。 偏好使用配接器來建立檢視,而不是直接建立檢視類別。 範圍配接器是建立檢視、更容易使用的方法,在某些情況下更有效率。

檢視類別特性

每個檢視類別主題在語法區段之後都有一個 特性 區段。 [特性]段有下列專案:

  • 範圍配接器:建立檢視的範圍配接器連結。 您通常會使用範圍配接器來建立檢視,而不是直接建立檢視類別,因此為了方便起見,這裡會列出它。

  • 基礎範圍:檢視對於可以使用的基礎範圍類型有不同的反覆運算器需求。 如需反覆運算器類型的詳細資訊,請參閱 反覆運算器階層 的範圍。

  • 檢視反覆運算器類別:檢視的反覆運算器類別。 當檢視調整範圍時,檢視的反覆運算器類型通常與基礎範圍的反覆運算器類型相同。 不過,某些檢視可能會有所不同。 例如, 具有 bidirectional_iteratorreverse_view即使基礎範圍有 random_access_iterator

  • 元素類型:檢視反覆運算器傳回的項目類型。

  • Sized:檢視是否可以傳回其參考的項目數目。 並非所有檢視都可以。

  • 通用範圍:指定檢視是否為 common_range,這表示開始反覆運算器和 sentinel 類型相同。 常見範圍適用於適用於反覆運算器配對的預先範圍程序代碼。 例如,序列容器的反覆運算器配對建構函式,例如 vector(ranges::begin(x), ranges::end(x))

  • 借用的範圍:指定檢視是否為借用的範圍。 borrowed_range<T>表示您可以在終結之後T使用反覆運算器T

    沒有標準容器是借用的範圍,因為終結容器會釋放元素並使任何反覆運算器失效。 在這種情況下,我們說反覆運算器在破壞后「懸而未定」。

    例如, std::ranges::find() 通常會將反覆運算器傳回至 range 自變數中找到的專案。 如果 range 自變數是暫時的(右值)容器,則儲存傳回的反覆運算器並稍後使用它是錯誤的,因為它「懸空」。

    傳回反覆運算器(或子範圍)的範圍演算法只有在其自變數為左值(非暫時)或借用的範圍時,才會執行此動作。 否則,它們會傳回 std::dangling 物件,這會在錯誤訊息中提供提示,說明如果您嘗試像反覆運算器一樣使用錯誤訊息。

  • 這是 const反覆運算的:指出您是否可以逐一 const 查看檢視的實例。 const並非所有檢視都可以進行查看。 如果檢視無法 const 逐一查看,則您無法逐一查看 for (const auto& element : as_const(theView)) ,或將它傳遞給接受 const 檢視參考的函式,然後嘗試逐一查看檢視。

範圍反覆運算器階層

在每個檢視類別主題的 [特性] 區段中,基礎範圍檢視反覆運算器類別中的反覆運算器類別是指範圍/檢視所支援的反覆運算器種類。 範圍反覆運算器有六種類別,這些類別是由 C++20 概念所識別。 範圍反覆運算器的階層,以增加功能的順序,是:

範圍反覆運算器概念 描述
output_range 僅限寫入,只會向前移動;單一傳遞。
input_range 只讀,只會向前移動;單一傳遞。
forward_range 只往前移動;多重傳遞。
bidirectional_range 可以向前和向後移動;多重傳遞。
random_access_range 可以使用索引存取集合;多重傳遞。
contiguous_range 可以存取具有索引的集合,而且元素會連續儲存在記憶體中。

一般而言,反覆運算器具有數據表中之前反覆運算器的功能。 例如, bidirectional_range 具有的功能 forward_range,但反之亦然。 除了 input_range,因為您無法寫入 至 input_range,因此沒有 的功能output_range

語句「需要 input_range 或更高」表示檢視可以搭配 input_rangeforward_range、、 bidirectional_rangerandom_access_rangecontiguous_range 反覆運算器使用,因為它們全都能夠做為 input_range

反覆運算器階層的範圍與反覆運算器階層直接相關。 如需詳細資訊,請參閱 反覆運算器概念

另請參閱

<ranges>
範圍配接器