<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_view C++20 |
輸入數據流中後續項目的檢視。 特製化包括 istream_view 和 wistream_view 。 |
common_view C++20 |
將具有不同反覆運算器/sentinel 類型的檢視調整為具有相同反覆運算器/sentinel 類型的檢視。 |
drop_view C++20 |
從另一個檢視建立,略過第一個 count 元素。 |
drop_while_view C++20 |
從另一個檢視建立,只要述詞保留,就會略過前置元素。 |
elements_view C++20 |
檢視所選取索引到集合中每個類似 Tuple 的值。 例如,假設有一系列 std::tuple<string, int> 值,請建立包含每個 Tuple 中所有 string 元素的檢視。 |
empty_view C++20 |
沒有元素的檢視。 |
filter_view C++20 |
篩選出不符合述詞的範圍元素。 |
iota_view C++20 |
產生的檢視,其中包含遞增值的序列。 |
join_view C++20 |
將多個範圍的所有元素合併成單一檢視。 |
keys_view C++20 |
檢視集合中每個類似 Tuple 的值的第一個索引。 例如,假設有一個值範圍 std::tuple<string, int> ,請建立檢視,其中包含每個 Tuple 中的 string 專案。 |
lazy_split_view C++20 |
根據分隔符將檢視分割成子範圍。 |
owning_view C++20 |
從另一個範圍取得項目的擁有權。 |
ref_view C++20 |
參考屬於另一個範圍之項目的檢視。 |
reverse_view C++20 |
以反向順序呈現範圍的專案。 |
single_view C++20 |
只包含一個項目的檢視。 |
split_view C++20 |
根據分隔符將檢視分割成子範圍。 |
subrange C++20 |
範圍元素的檢視,如開始反覆運算器和 sentinel 所定義。 |
take_view C++20 |
包含從範圍前面擷取的指定項目數目。 |
take_while_view C++20 |
包含符合指定述詞之範圍的前置元素。 |
transform_view C++20 |
轉換函式套用至每個元素之後,基礎序列的檢視。 |
values_view C++20 |
檢視集合中每個類似 Tuple 的值的第二個索引。 例如,假設有一個值範圍 std::tuple<string, int> ,請建立檢視,其中包含每個 Tuple 中的 int 專案。 |
其中許多類別在命名空間中有std:views
對應的範圍配接器,可建立它們的實例。 偏好使用配接器來建立檢視,而不是直接建立檢視類別。 範圍配接器是建立檢視、更容易使用的方法,在某些情況下更有效率。
檢視類別特性
每個檢視類別主題在語法區段之後都有一個 特性 區段。 [特性] 區段有下列專案:
範圍配接器:建立檢視的範圍配接器連結。 您通常會使用範圍配接器來建立檢視,而不是直接建立檢視類別,因此為了方便起見,這裡會列出它。
基礎範圍:檢視對於可以使用的基礎範圍類型有不同的反覆運算器需求。 如需反覆運算器類型的詳細資訊,請參閱 反覆運算器階層 的範圍。
檢視反覆運算器類別:檢視的反覆運算器類別。 當檢視調整範圍時,檢視的反覆運算器類型通常與基礎範圍的反覆運算器類型相同。 不過,某些檢視可能會有所不同。 例如, 具有
bidirectional_iterator
,reverse_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_range
、 forward_range
、、 bidirectional_range
、 random_access_range
或 contiguous_range
反覆運算器使用,因為它們全都能夠做為 input_range
。
反覆運算器階層的範圍與反覆運算器階層直接相關。 如需詳細資訊,請參閱 反覆運算器概念。