文章目錄
- 引言
- C++23 范圍庫概述
- 范圍視圖(Range Views)
- 范圍算法(Range Algorithms)
- 范圍適配器(Range Adapters)
- std::views::chunk_by 介紹
- 基本概念
- 特性
- 使用場景
- 示例代碼
- 簡單示例
- 自定義謂詞示例
- 總結
引言
在C++的發展歷程中,每一個新版本都會帶來一些令人期待的特性和改進。C++23作為C++20的增量更新,聚焦于簡化代碼、增強類型安全和填補功能缺口。其中,std::views::chunk_by
作為范圍適配器的新成員,為我們處理數據序列提供了更加便捷和高效的方式。本文將詳細介紹 std::views::chunk_by
的基本概念、特性、使用場景以及示例代碼。
C++23 范圍庫概述
在深入了解 std::views::chunk_by
之前,我們先來簡單回顧一下C++20引入的范圍庫(Ranges library)。范圍庫是C++20的一個主要組成部分,它提供了一種新的方法來處理容器中的元素序列,使得代碼更加簡潔和可讀。范圍庫主要包含以下幾個關鍵概念:
范圍視圖(Range Views)
范圍視圖提供了一種不修改原始數據的情況下,對容器進行變換、過濾和切片的能力。常見的視圖包括 std::views::all
、std::views::filter
、std::views::transform
等。例如:
#include <iostream>
#include <vector>
#include <ranges>int main() {std::vector<int> vec = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};auto even_numbers = vec | std::views::filter([](int x) { return x % 2 == 0; });auto squared = even_numbers | std::views::transform([](int x) { return x * x; });auto first_three = squared | std::views::take(3);for (int x : first_three) {std::cout << x << " ";}// 輸出: 4 16 36 return 0;
}
范圍算法(Range Algorithms)
C++20引入了新的范圍版本的算法,這些算法可以直接在范圍對象上操作,而不需要顯式循環。例如 std::ranges::sort
、std::ranges::find
等。示例如下:
#include <iostream>
#include <vector>
#include <ranges>int main() {std::vector<int> vec = {5, 3, 1, 4, 2};std::ranges::sort(vec);std::cout << "Sorted vector: ";for (int x : vec) {std::cout << x << " ";}// 輸出: Sorted vector: 1 2 3 4 5 auto min_val = std::ranges::min(vec);std::cout << "\nMinimum value: " << min_val << std::endl;// 輸出: Minimum value: 1 return 0;
}
范圍適配器(Range Adapters)
范圍適配器類似于視圖,但它們通常用于創建新的范圍對象。常見的適配器包括 std::ranges::views::join
、std::ranges::views::chunk_by_size
等。
std::views::chunk_by 介紹
基本概念
std::views::chunk_by
是C++23中引入的一個范圍適配器,它接受一個視圖和一個可調用對象(二元謂詞),并通過在每對相鄰元素之間拆分底層視圖來生成一個子范圍(塊)的視圖。對于這些元素,謂詞返回 false
。每對元素中的第一個元素屬于前一個塊,第二個元素屬于下一個塊。
在標題 <ranges>
中定義如下:
template< ranges::forward_range V, std::indirect_binary_predicate<iterator_t<V>, ranges::iterator_t<V>> Pred >requires ranges::view<V> && std::is_object_v<Pred>
class chunk_by_view: public ranges::view_interface<chunk_by_view<V, Pred>> (since C++23)
namespace views {inline constexpr /* unspecified */ chunk_by = /* 未指定 */ ;
} (since C++23)
特性
- 謂詞而非等價關系:與D語言的
chunkBy
不同,std::views::chunk_by
不要求謂詞是一個等價關系。例如,D的chunkBy
要求謂詞滿足自反性、對稱性和傳遞性,而std::views::chunk_by
則沒有這個限制。 - 不支持輸入范圍:由于
chunk_by
需要對相鄰元素計算謂詞,因此它要求底層范圍是前向范圍(forward range)。緩存元素不是一種可行的方法,主要原因在 [P2321R2] 的第4.3.4節中有討論。 - 范圍屬性:如果底層范圍是雙向范圍(bidirectional range),則
chunk_by
也是雙向范圍;否則,它是前向范圍。如果底層范圍是通用范圍(common range),則chunk_by
也是通用范圍。它永遠不會是借用范圍(borrowed range)或有大小范圍(sized range)。 - 緩存機制:類似于
split
,chunk_by
在其begin
中計算第一個范圍的末尾并進行緩存,以滿足攤銷的 O ( 1 ) O(1) O(1) 要求。這意味著它不支持常量迭代。
使用場景
std::views::chunk_by
適用于需要將一個序列按照某種規則進行分組的場景。例如,將一個整數序列按照奇偶性進行分組,或者將一個字符串序列按照長度進行分組等。
示例代碼
簡單示例
#include <iostream>
#include <vector>
#include <ranges>
#include <fmt/core.h>int main() {std::vector<int> v = {1, 2, 2, 3, 0, 4, 5, 2};fmt::print("{}\n", v | std::views::chunk_by(std::ranges::less_equal{})); // [[1, 2, 2, 3], [0, 4, 5], [2]]return 0;
}
在這個示例中,我們使用 std::views::chunk_by
和 std::ranges::less_equal{}
謂詞將整數序列 v
進行分組。當相鄰元素不滿足小于等于關系時,就會進行分組。
自定義謂詞示例
#include <iostream>
#include <vector>
#include <ranges>
#include <fmt/core.h>int main() {std::vector<int> v = {1, 3, 5, 2, 4, 6, 7, 9};auto is_odd = [](int x) { return x % 2 == 1; };auto chunked = v | std::views::chunk_by([&is_odd](int a, int b) { return is_odd(a) == is_odd(b); });for (const auto& chunk : chunked) {for (int x : chunk) {std::cout << x << " ";}std::cout << std::endl;}return 0;
}
在這個示例中,我們自定義了一個謂詞 is_odd
來判斷一個數是否為奇數。然后使用 std::views::chunk_by
將整數序列按照奇偶性進行分組。
總結
std::views::chunk_by
是C++23范圍庫中一個非常實用的范圍適配器,它為我們處理數據序列提供了更加靈活和高效的方式。通過使用 std::views::chunk_by
,我們可以輕松地將一個序列按照某種規則進行分組,而不需要手動編寫復雜的循環和邏輯。同時,范圍庫的惰性求值特性也使得代碼更加高效,避免了不必要的計算和內存消耗。在實際開發中,我們可以根據具體的需求靈活運用 std::views::chunk_by
來簡化代碼,提高開發效率。