分享方式:


<ranges>

概括而言,範圍是您可以逐一查看的範圍。 範圍是由反覆運算器表示,這個反覆運算器會標記範圍的開頭,以及標記範圍結尾的 sentinel。 sentinel 的類型可能與開始反覆運算器相同,或可能不同。 C++標準連結庫中的容器,例如 vectorlist是範圍。 範圍會以簡化和放大您使用標準範本連結庫 (STL) 的能力的方式抽象化反覆運算器。

STL 演算法通常會採用指向其應該運作之集合部分的反覆運算器。 例如,請考慮如何使用 排序 vector std::sort()。 您可以傳遞兩個反覆運算器,以標記 的 vector開頭和結尾。 這可提供彈性,但將反覆運算器傳遞至演算法是額外的工作,因為您可能只想排序整個專案。

透過範圍,您可以呼叫 std::ranges::sort(myVector);,這會被視為您呼叫 std::sort(myVector.begin(), myVector.end());。 在範圍連結庫中,演算法會採用範圍做為參數(雖然它們也可以視需要採用反覆運算器)。 它們可以直接在集合上操作。 中<algorithm>可用的範圍演算法範例包括 copycopy_ncopy_if、、all_ofany_ofcount_iffor_eachfindfind_if_notfind_iffor_each_ncountnone_ofequal和 。mismatch

但也許範圍最重要的優點是,您可以撰寫 STL 演算法,以讓人聯想到功能程式設計的樣式在範圍上運作。

範圍範例

在範圍之前,如果您想要轉換符合特定準則的集合元素,您需要引進中繼步驟來保存作業之間的結果。 例如,如果您想要從另一個向量中的元素建置正方形的向量,而該向量由三個可分割,您可以撰寫類似下列內容:

std::vector<int> input = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
std::vector<int> intermediate, output;

std::copy_if(input.begin(), input.end(), std::back_inserter(intermediate), [](const int i) { return i%3 == 0; });
std::transform(intermediate.begin(), intermediate.end(), std::back_inserter(output), [](const int i) {return i*i; });

透過範圍,您可以完成相同的工作,而不需要 intermediate 向量:

// requires /std:c++20
std::vector<int> input = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

auto output = input
    | std::views::filter([](const int n) {return n % 3 == 0; })
    | std::views::transform([](const int n) {return n * n; });

除了更容易閱讀,此程式代碼可避免向量及其內容所需的 intermediate 記憶體配置。 它也可讓您撰寫兩項作業。

在上述程式代碼中,每一個以三個分隔的元素都會與作業合併為該元素的正方形。 管道 (|) 符號會將作業鏈結在一起,並且由左至右讀取。

結果 output本身是一種稱為 檢視的範圍。

檢視

檢視是輕量型範圍。 檢視作業,例如預設建構、移動建構/指派、複製建構/指派(如果有)、解構、開始和結束,不論檢視中的元素數目為何,都會在固定時間內發生。

檢視是由範圍配接器所建立,如下一節討論。 如需實作各種檢視之類別的詳細資訊,請參閱 檢視類別

檢視中的元素顯示方式取決於您用來建立檢視的範圍配接器。 在上一個範例中,範圍配接器會採用範圍,並傳回三個可分割之元素的檢視。 基礎範圍不變。

檢視是可組合的,其功能強大。 在上一個範例中,三個可分割的向量元素檢視會與將這些專案平方的檢視結合。

檢視的元素會延遲評估。 也就是說,在您要求元素之前,才會評估您套用至檢視中每個元素的轉換。 例如,如果您在調試程式中執行下列程序代碼,並在行上auto divisible_by_three = ...auto square = ...放置斷點,您會看到您叫用 divisible_by_three Lambda 斷點,因為 中的input每個元素都經過三個測試,以取得可除性。 Lambda square 斷點會叫用,因為三個可分割的元素會平方。

// requires /std:c++20
#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 << '\n';
    }
    return 0;
}

如需檢視的詳細資訊,請參閱 <ranges> 檢視類別

範圍配接器

範圍配接器會採用範圍併產生檢視。 範圍配接器會產生延遲評估的檢視。 也就是說,您不會產生轉換範圍中每個元素以產生檢視的成本。 當您存取該專案時,您只需支付在檢視中處理元素的成本。

在上一個範例中 filter ,範圍配接器會建立名為 input 的檢視,其中包含三個可分割的元素。 transform範圍配接器會接受以三個分隔的元素檢視,並建立這些元素平方的檢視。

範圍配接器可以鏈結在一起(組成),這是範圍的力量和靈活性的核心。 撰寫範圍配接器可讓您克服先前 STL 演算法不容易撰寫的問題。

如需建立檢視的詳細資訊,請參閱 範圍配接器

範圍演算法

某些範圍演算法採用範圍自變數。 例如 std::ranges::sort(myVector);

範圍演算法幾乎與命名空間中 std 對應的反覆運算器配對演算法完全相同。 差別在於它們具有 概念強制執行的條件約束,而且它們接受範圍自變數或更多反覆運算器-sentinel 自變數組。 它們可以直接在容器上工作,而且可以輕鬆地鏈結在一起。

<ranges> 函式

下列函式可用來建立範圍的反覆運算器和 Sentinels,以及取得範圍的大小。

函式 描述
beginC++20 取得範圍中第一個專案的反覆運算器。
cbeginC++20 取得範圍中第一個專案 const 的反覆運算器。
cendC++20 取得限定範圍結尾的 constsentinel。
cdataC++20 取得連續範圍中第一個專案 const 的指標。
crbeginC++20 取得範圍開頭的反向 const 反覆運算器。
crendC++20 取得傳回內容 crbegin() 結尾的 sentinel。
dataC++20 取得連續範圍中第一個專案的指標。
emptyC++20 判斷範圍是否為空白。
endC++20 取得範圍結尾的 sentinel。
rbeginC++20 取得範圍開頭的反向反覆運算器。
rendC++20 取得範圍結尾之 sentinel 的反向反覆運算器。
sizeC++20 取得範圍的大小做為不帶正負號的值。
ssizeC++20 取得範圍的大小做為帶正負號的值。

如需詳細資訊,請參閱 <ranges> 函式

範圍概念

您逐一查看範圍元素的方式取決於其基礎反覆運算器類型。 範圍會使用C++概念來指定其支持的反覆運算器。

在 C++20 中,假設概念 X 會精簡概念 Y,表示滿足概念 Y 的所有專案也滿足概念 X。例如:汽車、公共汽車和卡車全部精簡車輛

某些範圍概念會鏡像反覆運算器類別的階層。 下表列出範圍概念,以及可套用的容器類型。

範圍概念 描述 支援的容器
std::ranges::output_range 可以向前反覆運算。
std::ranges::input_range 可以至少從頭到尾反覆運算一次。 std::forward_list
std::unordered_map
std::unordered_multimap
std::unordered_set
std::unordered_multiset
basic_istream_view
std::ranges::forward_range 可以從頭到尾反覆運算一次以上。 std::forward_list
std::unordered_map
std::unordered_multimap
std::unordered_set
std::unordered_multiset
std::ranges::bidirectional_range 可以向前和向後反覆運算一次以上。 std::list
std::map
std::multimap
std::multiset
std::set
std::ranges::random_access_range 可以使用運算符來存取任意專案(以常數時間為單位)。[] std::deque
std::ranges::contiguous_range 元素會連續儲存在記憶體中。 std::array
std::string
std::vector

如需這些概念的詳細資訊,請參閱 <ranges> 念。

<ranges> 別名範本

下列別名範本會決定某個範圍的反覆運算器和 Sentinels 類型:

別名範本 描述
borrowed_iterator_tC++20 判斷 傳回給 range 的反覆運算器是否是指其存留期已結束的範圍。
borrowed_subrange_tC++20 判斷傳回給 subrange 的反覆運算器是否是指其存留期已結束的子範圍。
danglingC++20 表示傳回的反覆運算器 range/subrange ,其存留期大於其所參考的 range/subrange 存留期。
iterator_tC++20 傳回指定之範圍類型的反覆運算器類型。
range_difference_tC++20 傳回指定範圍反覆運算器類型的差異型別。
range_reference_tC++20 傳回指定之範圍反覆運算器類型的參考型別。
range_rvalue_reference_tC++20 傳回指定範圍反覆運算器類型的右值參考型別。 換句話說,範圍元素的右值參考類型。
range_size_tC++20 傳回用來報告指定範圍大小的型別。
range_value_tC++20 傳回指定之範圍反覆運算器類型的實值型別。 或者換句話說,範圍中的項目類型。
sentinel_tC++20 傳回指定之範圍的 sentinel 型別。

如需這些別名範本的詳細資訊,請參閱 <ranges> 別名範本

另請參閱

<ranges> 函數
<ranges> 概念
範圍配接器
標頭文件參考