C++20 Range類型的具體使用
一、引言
在 C++ 編程中,經(jīng)常需要處理序列化的數(shù)據(jù),例如數(shù)組、列表或其他容器中的元素。為了高效且簡潔地操作這些序列,C++20 引入了 Ranges 庫,它提供了一種更強(qiáng)大、更靈活的方式來表達(dá)和處理數(shù)據(jù)序列。本篇將深入探討 Range 的各種概念,例如 input_range, forward_range, bidirectional_range, random_access_range 等,以及它們之間的區(qū)別和應(yīng)用場景,更好地理解和運(yùn)用 Ranges 庫。
Range 的定義和核心思想:Range 是一種對序列的抽象,它代表一個可以迭代訪問其元素的集合。不同于傳統(tǒng)的迭代器對(begin() 和 end()),Range 將序列視為一個單一的實體,簡化了序列處理。一個 Range 通常由一個起始迭代器和一個結(jié)束迭代器(或哨兵)定義,用于標(biāo)記序列的邊界。
Ranges 庫的引入極大地提高了代碼的可讀性和表達(dá)能力。它允許以更聲明式的方式編寫代碼,專注于操作的邏輯而不是底層的迭代細(xì)節(jié)。
- Ranges 支持惰性求值,只有在真正需要時才會計算結(jié)果。這種特性可以顯著提高代碼的效率,尤其是在處理大型數(shù)據(jù)集時。
- Ranges 還可以輕松地組合各種視圖,例如過濾、轉(zhuǎn)換和排序,而不會產(chǎn)生額外的性能開銷。這使得可以以一種非常簡潔和靈活的方式構(gòu)建復(fù)雜的數(shù)據(jù)處理管道。
為什么需要深入理解 Range 的類型?
- 不同的 Range 類型提供了不同的能力,例如單次遍歷、多次遍歷、隨機(jī)訪問等。理解這些類型的區(qū)別可以幫助選擇最合適的算法,并最大程度地提高代碼的性能。使用錯誤的 Range 類型會導(dǎo)致編譯錯誤或運(yùn)行時錯誤。
- 許多 C++ 標(biāo)準(zhǔn)庫算法和第三方庫已經(jīng)開始支持 Ranges。深入理解 Range 的類型可以幫助更好地利用這些庫,并編寫更簡潔、更高效的代碼。
二、Range 概念基礎(chǔ)
在深入探討各種 Range 類型之前,需要先了解 Range 的一些基本概念和特征。雖然前面文章都介紹了很多次,但這里還是要簡單回顧一下,有助于更好地理解不同 Range 類型之間的區(qū)別和聯(lián)系。
迭代器與 Range 的關(guān)系:Range 的核心在于迭代器。每個 Range 都可以通過一對迭代器(或一個迭代器和一個哨兵)來表示。起始迭代器指向 Range 的第一個元素,結(jié)束迭代器(或哨兵)指向 Range 結(jié)尾的下一個位置。
Range 的能力由其迭代器的能力決定。例如,如果一個 Range 的迭代器支持雙向移動,那么這個 Range 就支持雙向遍歷。
Range 的視圖 (View) 提供了一種對 Range 進(jìn)行非破壞性操作的方式。視圖并不會復(fù)制底層的 Range 數(shù)據(jù),而是提供了一個新的視角來觀察和操作數(shù)據(jù)。
視圖通?;诹硪粋€ Range 創(chuàng)建,并提供不同的觀察方式,例如轉(zhuǎn)換、過濾或排序。視圖是惰性求值的,只有在需要時才會計算結(jié)果。這可以提高代碼的效率,尤其是在處理大型數(shù)據(jù)集。多個視圖可以組合在一起,形成一個數(shù)據(jù)處理管道,而不會產(chǎn)生額外的性能開銷。
三、深入理解 Range 的各種類型
這里詳細(xì)介紹幾種 Range 類型,包括 input_range,forward_range,bidirectional_range 和 random_access_range,并解釋它們之間的區(qū)別、特性以及應(yīng)用場景。
3.1、input_range (輸入范圍)
input_range 是最基本的 Range 類型。它的迭代器只能單次向前移動,即只能遍歷 input_range 一次;每次遞增迭代器后,之前迭代器所指向的元素可能會失效。
無法保證不同迭代器指向相同的元素時,它們的值是否相等。
input_range 適用于處理只能讀取一次的數(shù)據(jù)流,例如從網(wǎng)絡(luò)套接字或傳感器讀取數(shù)據(jù)。
典型操作:
- 讀取元素:可以使用
*it獲取當(dāng)前迭代器指向的元素值。 - 前移迭代器:可以使用
++it將迭代器移動到下一個元素。 - 不支持多次遍歷:一旦遍歷完成,就不能再次遍歷同一個
input_range。 input_range的迭代器不支持寫入操作。
應(yīng)用場景:
- 一次性處理數(shù)據(jù)流:例如,從輸入流(例如文件或網(wǎng)絡(luò)流)讀取數(shù)據(jù)。
- 傳感器數(shù)據(jù)讀?。簭膫鞲衅髯x取數(shù)據(jù)流,這些數(shù)據(jù)通常只能讀取一次。
示例:
#include <iostream>
#include <vector>
#include <ranges>
#include <algorithm>
#include <list>
#include <sstream>
#include <iterator>
void demonstrate_input_range()
{
std::istringstream input_stream("1 2 3 4 5 6");
std::ranges::input_range auto int_range = std::ranges::istream_view<int>(input_stream);
std::cout << "從 input stream 讀取數(shù)據(jù): " << std::endl;
for (int value : int_range) {
std::cout << value << " ";
}
std::cout << std::endl;
// 嘗試再次遍歷 input_range (這將輸出未定義的內(nèi)容,因為 input_range 只能遍歷一次)
std::cout << "再次遍歷 input stream: "<< std::endl;
for (int value : int_range) {
std::cout << value << " ";
}
std::cout << std::endl;
std::cout << "使用 std::ranges::for_each 消費(fèi) input_range" << std::endl;
std::istringstream input_stream2("6 7 8 9 10");
std::ranges::input_range auto int_range2 = std::ranges::istream_view<int>(input_stream2);
std::ranges::for_each(int_range2, [](int value) { std::cout << value * 2 << " "; });
std::cout << std::endl;
}
int main()
{
demonstrate_input_range();
return 0;
}
輸出內(nèi)容:
從 input stream 讀取數(shù)據(jù):
1 2 3 4 5 6
再次遍歷 input stream:使用 std::ranges::for_each 消費(fèi) input_range
12 14 16 18 20
3.2、forward_range (前向范圍)
forward_range 擴(kuò)展了 input_range 的功能,它支持多次向前遍歷 Range 中的元素。即可以多次遍歷同一個 forward_range,并且每次遍歷的結(jié)果都是一致的。
forward_range 的迭代器是多遍的,可以保存迭代器的副本,并在稍后使用它重新訪問相同的元素。遞增迭代器不會使其他迭代器失效。
與 input_range 的區(qū)別:
forward_range具備input_range的所有能力,即單次向前遍歷和讀取元素。forward_range的主要區(qū)別在于它支持多次遍歷,而input_range只支持單次遍歷。 這是因為forward_range的迭代器是多遍的,而input_range的迭代器是單遍的。
典型操作:
- 可以多次循環(huán)遍歷
forward_range中的元素。 - 可以使用
std::ranges::find等算法在forward_range中查找特定元素。 - 可以使用
std::ranges::count等算法統(tǒng)計forward_range中滿足特定條件的元素個數(shù)。
需要多次遍歷同一序列的應(yīng)用場景:
- 對數(shù)據(jù)進(jìn)行多次分析或處理。
- 在數(shù)據(jù)中查找多個元素。
- 緩存數(shù)據(jù)以便后續(xù)使用。
許多標(biāo)準(zhǔn)庫算法都需要 forward_range,例如 std::ranges::sort (雖然它需要更強(qiáng)的 random_access_range), std::ranges::find,std::ranges::count 等。
示例:
#include <iostream>
#include <vector>
#include <ranges>
#include <algorithm>
#include <list>
#include <sstream>
#include <iterator>
// 演示 forward_range
void demonstrate_forward_range()
{
std::vector<int> numbers = {1, 2, 3, 4, 5};
// 多次遍歷 forward_range
std::cout << "第一次遍歷 forward_range: ";
for (int number : numbers) {
std::cout << number << " ";
}
std::cout << std::endl;
std::cout << "第二次遍歷 forward_range: ";
for (int number : numbers) {
std::cout << number << " ";
}
std::cout << std::endl;
// 使用 std::ranges::find 查找元素
auto it = std::ranges::find(numbers, 3);
if (it != numbers.end()) {
std::cout << "找到元素 3" << std::endl;
}
}
int main()
{
demonstrate_forward_range();
return 0;
}
結(jié)果輸出:
第一次遍歷 forward_range: 1 2 3 4 5
第二次遍歷 forward_range: 1 2 3 4 5
找到元素 3
3.3、bidirectional_range
定義和特性:
bidirectional_range擴(kuò)展了forward_range的功能,支持向前和向后遍歷元素。bidirectional_range的迭代器可以遞增 (++it) 和遞減 (--it)。- 與
forward_range一樣,bidirectional_range的迭代器也是多遍的。
與 forward_range 的區(qū)別:
bidirectional_range具備forward_range的所有能力,包括多次向前遍歷和讀取元素。bidirectional_range的主要區(qū)別在于它支持反向遍歷,而forward_range只支持向前遍歷。
典型操作:
- 正向遍歷:與
forward_range相同。 - 反向遍歷:可以使用反向迭代器 (
rbegin()和rend()) 進(jìn)行反向遍歷。 - 雙向查找:可以向前和向后搜索元素。
應(yīng)用場景:
std::list是一個典型的雙向范圍。- 需要反向處理序列的場景。
- 某些算法需要雙向遍歷的能力,例如反向排序。
示例:
#include <iostream>
#include <vector>
#include <ranges>
#include <algorithm>
#include <list>
#include <sstream>
#include <iterator>
void demonstrate_bidirectional_range()
{
std::list<int> numbers = {1, 2, 3, 4, 5};
// 正向遍歷 bidirectional_range
std::cout << "正向遍歷 bidirectional_range: ";
for (int number : numbers) {
std::cout << number << " ";
}
std::cout << std::endl;
// 反向遍歷 bidirectional_range
std::cout << "反向遍歷 bidirectional_range: ";
for (auto it = numbers.rbegin(); it != numbers.rend(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
// 使用 std::ranges::find 查找元素 (正向)
auto it = std::ranges::find(numbers, 3);
if (it != numbers.end()) {
std::cout << "正向找到元素 3" << std::endl;
}
// 使用 std::ranges::find 查找元素 (反向) - 需要使用 std::ranges::reverse_view
auto reversed_numbers = std::ranges::reverse_view{numbers};
auto it_reverse = std::ranges::find(reversed_numbers, 3);
if (it_reverse != reversed_numbers.end()) {
std::cout << "反向找到元素 3" << std::endl;
}
}
int main()
{
demonstrate_bidirectional_range();
return 0;
}
3.4、random_access_range
定義和特性:
random_access_range擴(kuò)展了bidirectional_range的功能,支持隨機(jī)訪問元素,就像數(shù)組一樣。- 可以使用索引運(yùn)算符
[]直接訪問 Range 中的任何元素。 random_access_range的迭代器支持指針?biāo)阈g(shù)運(yùn)算,例如it + n、it - n、it[n]、it1 - it2等。- 迭代器也支持比較運(yùn)算符
<、>、<=、>=。
與 bidirectional_range 的主要區(qū)別在于random_access_range支持高效的隨機(jī)訪問,而 bidirectional_range 需要通過多次遞增或遞減迭代器來訪問特定元素。
典型操作:
- 索引訪問:可以使用
[]運(yùn)算符直接訪問元素。 - 排序:可以使用
std::ranges::sort等算法對random_access_range進(jìn)行排序。 - 二分查找:可以使用
std::ranges::binary_search等算法在已排序的random_access_range中進(jìn)行高效的二分查找。
應(yīng)用場景舉例:
std::vector、std::array、std::deque是典型的隨機(jī)訪問范圍。- 需要快速訪問任意元素的場景。
- 許多算法需要隨機(jī)訪問的能力,例如排序、二分查找等。
示例:
#include <iostream>
#include <vector>
#include <ranges>
#include <algorithm>
#include <list>
#include <array>
#include <sstream>
#include <iterator>
void demonstrate_random_access_range()
{
std::vector<int> numbers = {1, 2, 3, 4, 5};
// 隨機(jī)訪問 random_access_range
std::cout << "隨機(jī)訪問 random_access_range: ";
std::cout << numbers[0] << " " << numbers[2] << std::endl;
// 使用 std::ranges::sort 排序
std::ranges::sort(numbers); // 注意:示例簡單,vector已排序
std::cout << "排序后的 random_access_range: ";
for (int number : numbers) {
std::cout << number << " ";
}
std::cout << std::endl;
// 使用 std::ranges::sort 反向排序
std::ranges::sort(numbers, std::greater<int>());
std::cout << "反向排序后的 random_access_range: ";
for (int number : numbers) {
std::cout << number << " ";
}
std::cout << std::endl;
}
int main()
{
demonstrate_random_access_range();
return 0;
}
輸出:
隨機(jī)訪問 random_access_range: 1 3
排序后的 random_access_range: 1 2 3 4 5
反向排序后的 random_access_range: 5 4 3 2 1
3.5、contiguous_range
contiguous_range 是 random_access_range 的一個特例,其元素在內(nèi)存中是連續(xù)存儲的??梢韵駭?shù)組一樣,通過指針?biāo)阈g(shù)直接訪問元素。
contiguous_range 的 data() 成員函數(shù)返回一個指向底層連續(xù)內(nèi)存塊的指針。高效的內(nèi)存訪問和緩存利用率是其主要優(yōu)勢。
contiguous_range 的主要區(qū)別在于它的元素保證在內(nèi)存中是連續(xù)的,而 random_access_range 不一定保證連續(xù)性 (例如 std::deque 在存儲大量元素時,底層可能不是連續(xù)的).
典型操作:
- 直接內(nèi)存訪問:通過
data()獲取指向底層數(shù)據(jù)的指針,然后使用指針?biāo)阈g(shù)進(jìn)行操作。 - 高效的數(shù)據(jù)復(fù)制:可以利用連續(xù)性進(jìn)行高效的內(nèi)存塊復(fù)制。
- 與 C 風(fēng)格 API 的互操作性:可以直接將
data()返回的指針傳遞給 C 風(fēng)格的函數(shù)。
應(yīng)用場景舉例:
std::vector、std::array、std::string是典型的連續(xù)范圍。- 需要高效內(nèi)存訪問和操作的場景。
- 與需要連續(xù)內(nèi)存塊的 C 風(fēng)格 API 交互的場景。
示例:
#include <iostream>
#include <vector>
#include <ranges>
#include <algorithm>
#include <list>
#include <array>
#include <string>
#include <sstream>
#include <iterator>
void demonstrate_contiguous_range()
{
std::vector<int> vec = {1, 2, 3, 4, 5};
std::array<int, 5> arr = {1, 2, 3, 4, 5};
std::string str = "hello";
// 使用 data() 獲取指向底層數(shù)據(jù)的指針
int* vec_ptr = vec.data();
int* arr_ptr = arr.data();
char* str_ptr = str.data();
// 使用指針?biāo)阈g(shù)遍歷 contiguous_range
std::cout << "使用指針遍歷 vector: ";
for (size_t i = 0; i < vec.size(); ++i) {
std::cout << *(vec_ptr + i) << " ";
}
std::cout << std::endl;
std::cout << "使用指針遍歷 array: ";
for (size_t i = 0; i < arr.size(); ++i) {
std::cout << *(arr_ptr + i) << " ";
}
std::cout << std::endl;
std::cout << "使用指針遍歷 string: ";
for (size_t i = 0; i < str.size(); ++i) {
std::cout << *(str_ptr + i);
}
std::cout << std::endl;
}
int main()
{
demonstrate_contiguous_range();
return 0;
}
輸出:
使用指針遍歷 vector: 1 2 3 4 5
使用指針遍歷 array: 1 2 3 4 5
使用指針遍歷 string: hello
四、類型之間的層次結(jié)構(gòu)與選擇
C++20 中的 Range 概念建立在迭代器類別之上,形成了一個明確的層次結(jié)構(gòu),旨在精確描述可迭代序列的能力。這種層次結(jié)構(gòu)意味著更高級別的 Range 概念包含了低級別 Range 的所有能力,從而允許算法根據(jù) Range 的能力自動選擇最有效的實現(xiàn)。
- std::ranges::input_range: 這是最基本的 Range 類型,適用于一次性消費(fèi)的序列。
- std::ranges::forward_range: 是一個比 input_range 更強(qiáng)的概念。其迭代器是可復(fù)制的,并且 begin() 可以被多次調(diào)用,允許對序列進(jìn)行多遍遍歷。
- std::ranges::bidirectional_range: 在 forward_range 的基礎(chǔ)上,增加了雙向遍歷的能力。
- std::ranges::random_access_range: 在 bidirectional_range 的基礎(chǔ)上,增加了隨機(jī)訪問的能力。
- std::ranges::contiguous_range: 這是最強(qiáng)的 Range 類型,表示其元素在內(nèi)存中是連續(xù)存儲的,并且大小相同,可以通過指針?biāo)阈g(shù)進(jìn)行訪問。
這種層次結(jié)構(gòu)的關(guān)鍵在于,如果一個 Range 滿足了某個更高級別的概念,那么它也自動滿足了所有比它低級別的概念,但 input_range 不包含 output_range 的寫入能力。
選擇合適的 Range 類型對于編寫高效、正確且可讀的 C++ 代碼至關(guān)重要。C++20 Ranges 庫通過概念在編譯時強(qiáng)制執(zhí)行這些要求,從而提高了代碼的健壯性。
性能考量:
- 算法的效率往往取決于其所操作的 Range 的能力。
- C++20 Ranges 算法會利用 Range 的概念信息,自動選擇最適合給定 Range 類型的算法實現(xiàn),從而在編譯時優(yōu)化性能。
- 雖然理論上使用更高級別的 Range 總是更好,但在某些特定場景下,如果僅僅需要簡單的單次遍歷,使用
input_range或output_range這樣的基本類型更合適,因為它避免了不必要的開銷或復(fù)雜性。
五、總結(jié)
C++20 引入的 Ranges 庫提供了一種強(qiáng)大且靈活的機(jī)制來處理序列數(shù)據(jù)。通過定義 input_range、forward_range、bidirectional_range、random_access_range 和 contiguous_range 等不同類型的 Range,Ranges 庫能夠更精確地表達(dá)序列的能力,從而實現(xiàn)更高效的算法選擇和執(zhí)行
C++20 Ranges 庫的設(shè)計理念是根據(jù) Range 的能力自動選擇最優(yōu)的算法實現(xiàn)。因此,選擇合適的 Range 類型不僅可以提高代碼的可讀性和可維護(hù)性,還能在編譯時優(yōu)化性能,避免不必要的開銷。
到此這篇關(guān)于C++20 Range類型的具體使用的文章就介紹到這了,更多相關(guān)C++20 Range類型內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++ 將數(shù)據(jù)轉(zhuǎn)為字符串的幾種方法
這篇文章主要介紹了C++ 將數(shù)據(jù)轉(zhuǎn)為字符串的幾種方法,十分的實用,有需要的小伙伴可以參考下。2015-06-06
在clion上配置libtorch開發(fā)環(huán)境的圖文詳解
這篇文章主要介紹了在clion上配置libtorch開發(fā)環(huán)境的圖文詳解,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-11-11
VC使用TerminateProcess結(jié)束進(jìn)程實例
這篇文章主要介紹了VC使用TerminateProcess結(jié)束進(jìn)程的方法,實例演示了TerminateProcess結(jié)束進(jìn)程的具體實現(xiàn)過程,在進(jìn)行VC應(yīng)用程序開發(fā)時非常具有實用價值,需要的朋友可以參考下2014-10-10
C++?重載運(yùn)算符在HotSpot?VM中的應(yīng)用小結(jié)
C++支持運(yùn)算符重載,對于Java開發(fā)者來說,這個可能比較陌生一些,因為Java不支持運(yùn)算符重載,下面介紹一下HotSpot?VM中的運(yùn)算符重載,感興趣的朋友跟隨小編一起看看吧2023-09-09
win10系統(tǒng)下?VS2019點(diǎn)云庫PCL1.12.0的安裝與配置教程
點(diǎn)云庫全稱是Point?Cloud?Library(PCL),是一個獨(dú)立的、大規(guī)模的、開放的2D/3D圖像和點(diǎn)云處理項目,這篇文章主要介紹了win10系統(tǒng)下?VS2019點(diǎn)云庫PCL1.12.0的安裝與配置,需要的朋友可以參考下2022-07-07
C語言多進(jìn)程創(chuàng)建和回收的實現(xiàn)實例
本文主要介紹了C語言多進(jìn)程中的孤兒進(jìn)程和僵尸進(jìn)程的創(chuàng)建及回收方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-11-11
va_list(),va_start(),va_arg(),va_end() 詳細(xì)解析
這些宏定義在stdarg.h中,所以用到可變參數(shù)的程序應(yīng)該包含這個頭文件.下面我們寫一個簡單的可變參數(shù)的函數(shù),該函數(shù)至少有一個整數(shù)參數(shù),第二個參數(shù)也是整數(shù),是可選的.函數(shù)只是打印這兩個參數(shù)的值2013-09-09

