<ranges>
概括而言,範圍是您可以逐一查看的範圍。 範圍是由反覆運算器表示,這個反覆運算器會標記範圍的開頭,以及標記範圍結尾的 sentinel。 sentinel 的類型可能與開始反覆運算器相同,或可能不同。 C++標準連結庫中的容器,例如 vector
和 list
是範圍。 範圍會以簡化和放大您使用標準範本連結庫 (STL) 的能力的方式抽象化反覆運算器。
STL 演算法通常會採用指向其應該運作之集合部分的反覆運算器。 例如,請考慮如何使用 排序 vector
std::sort()
。 您可以傳遞兩個反覆運算器,以標記 的 vector
開頭和結尾。 這可提供彈性,但將反覆運算器傳遞至演算法是額外的工作,因為您可能只想排序整個專案。
透過範圍,您可以呼叫 std::ranges::sort(myVector);
,這會被視為您呼叫 std::sort(myVector.begin(), myVector.end());
。 在範圍連結庫中,演算法會採用範圍做為參數(雖然它們也可以視需要採用反覆運算器)。 它們可以直接在集合上操作。 中<algorithm>
可用的範圍演算法範例包括 copy
、copy_n
、copy_if
、、all_of
any_of
、count_if
for_each
find
find_if_not
find_if
for_each_n
count
none_of
、 equal
和 。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 自變數組。 它們可以直接在容器上工作,而且可以輕鬆地鏈結在一起。
下列函式可用來建立範圍的反覆運算器和 Sentinels,以及取得範圍的大小。
函式 | 描述 |
---|---|
begin C++20 |
取得範圍中第一個專案的反覆運算器。 |
cbegin C++20 |
取得範圍中第一個專案 const 的反覆運算器。 |
cend C++20 |
取得限定範圍結尾的 const sentinel。 |
cdata C++20 |
取得連續範圍中第一個專案 const 的指標。 |
crbegin C++20 |
取得範圍開頭的反向 const 反覆運算器。 |
crend C++20 |
取得傳回內容 crbegin() 結尾的 sentinel。 |
data C++20 |
取得連續範圍中第一個專案的指標。 |
empty C++20 |
判斷範圍是否為空白。 |
end C++20 |
取得範圍結尾的 sentinel。 |
rbegin C++20 |
取得範圍開頭的反向反覆運算器。 |
rend C++20 |
取得範圍結尾之 sentinel 的反向反覆運算器。 |
size C++20 |
取得範圍的大小做為不帶正負號的值。 |
ssize C++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>
概 念。
下列別名範本會決定某個範圍的反覆運算器和 Sentinels 類型:
別名範本 | 描述 |
---|---|
borrowed_iterator_t C++20 |
判斷 傳回給 range 的反覆運算器是否是指其存留期已結束的範圍。 |
borrowed_subrange_t C++20 |
判斷傳回給 subrange 的反覆運算器是否是指其存留期已結束的子範圍。 |
dangling C++20 |
表示傳回的反覆運算器 range /subrange ,其存留期大於其所參考的 range /subrange 存留期。 |
iterator_t C++20 |
傳回指定之範圍類型的反覆運算器類型。 |
range_difference_t C++20 |
傳回指定範圍反覆運算器類型的差異型別。 |
range_reference_t C++20 |
傳回指定之範圍反覆運算器類型的參考型別。 |
range_rvalue_reference_t C++20 |
傳回指定範圍反覆運算器類型的右值參考型別。 換句話說,範圍元素的右值參考類型。 |
range_size_t C++20 |
傳回用來報告指定範圍大小的型別。 |
range_value_t C++20 |
傳回指定之範圍反覆運算器類型的實值型別。 或者換句話說,範圍中的項目類型。 |
sentinel_t C++20 |
傳回指定之範圍的 sentinel 型別。 |
如需這些別名範本的詳細資訊,請參閱 <ranges>
別名範本。