Skip to content

Ch 27: C++20 Ranges 范围库

  • 理解 Ranges 库的核心优势
  • 掌握常用视图操作(filter、transform、take、drop)
  • 学会构建管道式数据处理
  • 理解惰性求值机制
  • 熟练使用 std::ranges 算法
# Python - 立即求值,创建中间列表
nums = range(1, 11) # 惰性生成器
evens = [x for x in nums if x % 2 == 0] # 创建列表
squares = [x**2 for x in evens] # 创建另一个列表
# squares = [4, 16, 36, 64, 100]
# 问题:创建了中间列表(内存浪费)
# 问题:无法处理无限序列
#include <vector>
#include <ranges>
#include <iostream>
std::vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// C++20 - 惰性求值,不创建中间容器
auto evens = nums | std::views::filter([](int x) { return x % 2 == 0; });
auto squares = evens | std::views::transform([](int x) { return x * x; });
// 实际计算只在迭代时发生
for (int n : squares) {
std::cout << n << " "; // 4 16 36 64 100
}
// 可以处理无限序列
auto infinite_odds = std::views::iota(1) // 1, 2, 3, ...
| std::views::filter([](int x) { return x % 2 != 0; }); // 1, 3, 5, ...
// take(5) 让无限序列变成有限
for (int n : infinite_odds | std::views::take(5)) {
std::cout << n << " "; // 1 3 5 7 9
}
#include <vector>
#include <algorithm>
#include <ranges>
std::vector<int> data = {3, 1, 4, 1, 5, 9, 2, 6};
// 传统方式:需要中间容器
std::vector<int> filtered;
std::copy_if(data.begin(), data.end(), std::back_inserter(filtered),
[](int x) { return x > 3; });
std::sort(filtered.begin(), filtered.end());
// Ranges 方式:惰性,简洁
auto result = data
| std::views::filter([](int x) { return x > 3; })
| std::views::common_view // 转为普通视图
| std::ranges::to<std::vector>; // C++23 收集到容器
#include <vector>
#include <ranges>
#include <iostream>
std::vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 过滤偶数
auto is_even = [](int x) { return x % 2 == 0; };
for (int n : nums | std::views::filter(is_even)) {
std::cout << n << " "; // 2 4 6 8 10
}
std::cout << "\n";
// 过滤大于 5 的数
for (int n : nums | std::views::filter([](int x) { return x > 5; })) {
std::cout << n << " "; // 6 7 8 9 10
}
#include <vector>
#include <ranges>
#include <iostream>
std::vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 组合:先过滤后转换
auto result = nums
| std::views::filter([](int x) { return x % 2 == 0; }) // 2, 4, 6, 8, 10
| std::views::transform([](int x) { return x * x; }); // 4, 16, 36, 64, 100
for (int n : result) {
std::cout << n << " ";
}
std::cout << "\n";
// 多次过滤
auto result2 = nums
| std::views::filter([](int x) { return x > 2; }) // 3-10
| std::views::filter([](int x) { return x < 8; }) // 3-7
| std::views::filter([](int x) { return x % 2 == 0; }); // 4, 6
for (int n : result2) {
std::cout << n << " "; // 4 6
}
#include <vector>
#include <ranges>
#include <iostream>
std::vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 方法1:使用 for 循环手动收集
std::vector<int> filtered;
for (int n : nums | std::views::filter([](int x) { return x % 2 == 0; })) {
filtered.push_back(n);
}
// 方法2:std::ranges::copy_to
std::vector<int> filtered2;
std::ranges::copy(nums | std::views::filter([](int x) { return x % 2 == 0; }),
std::back_inserter(filtered2));
// 方法3:C++23 ranges::to
// std::vector<int> filtered3 = nums
// | std::views::filter([](int x) { return x % 2 == 0; })
// | std::ranges::to<std::vector>(); // C++23
#include <vector>
#include <ranges>
#include <iostream>
#include <string>
std::vector<int> nums = {1, 2, 3, 4, 5};
// 平方
for (int n : nums | std::views::transform([](int x) { return x * x; })) {
std::cout << n << " "; // 1 4 9 16 25
}
std::cout << "\n";
// 字符串转换
std::vector<std::string> words = {"hello", "world"};
for (const std::string& s : words | std::views::transform([](const std::string& x) {
std::string r = x;
r[0] = std::toupper(r[0]);
return r;
})) {
std::cout << s << " "; // Hello World
}
std::cout << "\n";
#include <vector>
#include <ranges>
#include <iostream>
std::vector<int> nums = {1, 2, 3, 4, 5};
// 完整管道:过滤 -> 转换 -> 变换
auto result = nums
| std::views::filter([](int x) { return x > 2; }) // 3, 4, 5
| std::views::transform([](int x) { return x * x; }) // 9, 16, 25
| std::views::transform([](int x) { return x + 1; }); // 10, 17, 26
for (int n : result) {
std::cout << n << " "; // 10 17 26
}
std::cout << "\n";
// 使用 std::views::common 将视图转为可重复迭代
auto view = nums
| std::views::filter([](int x) { return x % 2 == 0; })
| std::views::transform([](int x) { return x * 10; });
// 多次迭代(需要 common_view 或 ref_view)
for (int n : view) {
std::cout << n << " ";
}
#include <views>
#include <iostream>
// iota - 生成无限或有限序列
// C++23 ranges 命名空间
for (int i : std::views::iota(1, 10)) { // 1 到 9
std::cout << i << " ";
}
std::cout << "\n";
// 1 2 3 4 5 6 7 8 9
// 从 0 开始,默认结束为无限
for (int i : std::views::iota(0) | std::views::take(5)) {
std::cout << i << " "; // 0 1 2 3 4
}
std::cout << "\n";
// 无限序列 + take
for (int i : std::views::iota(1)
| std::views::filter([](int x) { return x % 2 == 0; })
| std::views::take(10)) {
std::cout << i << " "; // 2 4 6 8 10 12 14 16 18 20
}
std::cout << "\n";
#include <vector>
#include <ranges>
#include <iostream>
#include <iomanip>
// 生成斐波那契数列(无限)
auto fibonacci = []() {
return std::views::iota(0)
| std::views::transform([](int n) {
// 斐波那契计算
if (n == 0) return 0;
if (n == 1) return 1;
int a = 0, b = 1;
for (int i = 2; i <= n; ++i) {
int temp = a + b;
a = b;
b = temp;
}
return b;
});
};
int main() {
std::cout << "Fibonacci: ";
for (int f : fibonacci() | std::views::take(15)) {
std::cout << f << " ";
}
std::cout << "\n";
// 生成质数
auto is_prime = [](int n) {
if (n < 2) return false;
for (int i = 2; i * i <= n; ++i) {
if (n % i == 0) return false;
}
return true;
};
std::cout << "Primes: ";
for (int p : std::views::iota(2) | std::views::filter(is_prime) | std::views::take(20)) {
std::cout << p << " ";
}
std::cout << "\n";
}
#include <vector>
#include <ranges>
#include <iostream>
std::vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// take - 取前 N 个
std::cout << "First 3: ";
for (int n : nums | std::views::take(3)) {
std::cout << n << " "; // 1 2 3
}
std::cout << "\n";
// drop - 跳过前 N 个
std::cout << "Skip first 3: ";
for (int n : nums | std::views::drop(3)) {
std::cout << n << " "; // 4 5 6 7 8 9 10
}
std::cout << "\n";
// drop_while - 跳过满足条件的元素
std::cout << "After first even: ";
for (int n : nums | std::views::drop_while([](int x) { return x % 2 != 0; })) {
std::cout << n << " "; // 2 3 4 5 6 7 8 9 10
}
std::cout << "\n";
// take_while - 取满足条件的元素
std::cout << "Until 5: ";
for (int n : nums | std::views::take_while([](int x) { return x < 5; })) {
std::cout << n << " "; // 1 2 3 4
}
std::cout << "\n";
#include <vector>
#include <ranges>
#include <iostream>
std::vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// reverse - 反向
std::cout << "Reversed: ";
for (int n : nums | std::views::reverse) {
std::cout << n << " "; // 10 9 8 7 6 5 4 3 2 1
}
std::cout << "\n";
// stride(C++23)- 步进
// for (int n : nums | std::views::stride(2)) {
// std::cout << n << " "; // 1 3 5 7 9 (C++23)
// }
// 组合:反向后取前3
std::cout << "Last 3: ";
for (int n : nums | std::views::reverse | std::views::take(3)) {
std::cout << n << " "; // 10 9 8
}
std::cout << "\n";
#include <vector>
#include <ranges>
#include <iostream>
#include <string>
std::string text = "hello world cpp";
// split(C++23)- 按分隔符分割
// for (auto word : text | std::views::split(' ')) {
// for (char c : word) {
// std::cout << c;
// }
// std::cout << "\n";
// }
// all - 将容器转为视图
std::vector<int> nums = {1, 2, 3, 4, 5};
auto view = std::views::all(nums);
for (int n : view) {
std::cout << n << " ";
}
std::cout << "\n";
#include <vector>
#include <ranges>
#include <iostream>
std::vector<int> nums = {1, 1, 2, 2, 2, 3, 1, 1};
// unique(C++23)- 去除相邻重复
// for (int n : nums | std::views::unique) {
// std::cout << n << " "; // 1 2 3 1
// }
// 注意:unique 只去除相邻的重复
// 要完全去重,需要先排序
#include <vector>
#include <ranges>
#include <iostream>
std::vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 顺序不同,结果可能不同!
// 先过滤后取 - 取前3个偶数
std::cout << "filter then take: ";
for (int n : nums
| std::views::filter([](int x) { return x % 2 == 0; })
| std::views::take(3)) {
std::cout << n << " "; // 2 4 6
}
std::cout << "\n";
// 先取后过滤 - 取前3个中过滤偶数
std::cout << "take then filter: ";
for (int n : nums
| std::views::take(3)
| std::views::filter([](int x) { return x % 2 == 0; })) {
std::cout << n << " "; // 2
}
std::cout << "\n";
#include <string>
#include <ranges>
#include <iostream>
#include <cctype>
std::string text = "Hello World";
// 转换为大写
std::string upper;
for (char c : text | std::views::transform(::toupper)) {
upper += c;
}
std::cout << upper << "\n"; // HELLO WORLD
// 统计字符
auto is_vowel = [](char c) {
c = std::tolower(c);
return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u';
};
int vowel_count = 0;
for (char c : text | std::views::filter(is_vowel)) {
++vowel_count;
}
std::cout << "Vowels: " << vowel_count << "\n";
// 字符串反转
std::string reversed;
for (char c : text | std::views::reverse) {
reversed += c;
}
std::cout << "Reversed: " << reversed << "\n"; // dlroW olleH
#include <vector>
#include <ranges>
#include <iostream>
#include <string>
#include <sstream>
struct Student {
std::string name;
int score;
};
std::vector<Student> students = {
{"Alice", 85},
{"Bob", 92},
{"Charlie", 78},
{"Diana", 95},
{"Eve", 88},
{"Frank", 72}
};
// 管道:过滤不及格 -> 按分数排序 -> 取前3 -> 提取姓名
std::cout << "Top 3 passing students:\n";
int rank = 1;
for (const auto& name : students
| std::views::filter([](const Student& s) { return s.score >= 80; })
| std::views::transform([](const Student& s) { return s.name; })
| std::views::transform([](const std::string& n) { return n + "!"; })
| std::views::take(3)) {
std::cout << rank++ << ". " << name << "\n";
}
// 分数统计
auto passing_scores = students
| std::views::filter([](const Student& s) { return s.score >= 80; })
| std::views::transform([](const Student& s) { return s.score; });
double avg = 0;
int count = 0;
for (int score : passing_scores) {
avg += score;
++count;
}
if (count > 0) {
avg /= count;
std::cout << "Average passing score: " << avg << "\n";
}
#include <vector>
#include <ranges>
#include <iostream>
std::vector<int> nums = {5, 3, 8, 1, 9, 2, 7};
// find - 找第一个满足条件的元素
auto it = std::ranges::find(nums, 8);
if (it != nums.end()) {
std::cout << "Found at index: " << std::distance(nums.begin(), it) << "\n";
}
// find_if - 找第一个满足谓词的元素
auto it2 = std::ranges::find_if(nums, [](int x) { return x > 6; });
if (it2 != nums.end()) {
std::cout << "First > 6: " << *it2 << "\n"; // 8
}
// find_if_not - 找第一个不满足谓词的元素
auto it3 = std::ranges::find_if_not(nums, [](int x) { return x < 5; });
if (it3 != nums.end()) {
std::cout << "First >= 5: " << *it3 << "\n"; // 5
}
#include <vector>
#include <ranges>
#include <iostream>
std::vector<int> nums = {1, 2, 3, 2, 4, 2, 5, 2};
// count - 计数等于指定值的元素
int twos = std::ranges::count(nums, 2);
std::cout << "Count of 2: " << twos << "\n"; // 4
// count_if - 计数满足条件的元素
int evens = std::ranges::count_if(nums, [](int x) { return x % 2 == 0; });
std::cout << "Count of evens: " << evens << "\n"; // 5
#include <vector>
#include <ranges>
#include <iostream>
std::vector<int> nums = {5, 2, 8, 1, 9, 3};
// sort - 排序
std::ranges::sort(nums);
for (int n : nums) {
std::cout << n << " ";
}
std::cout << "\n";
// is_sorted - 检查是否已排序
std::cout << "is_sorted: " << std::ranges::is_sorted(nums) << "\n"; // 1
// partial_sort - 部分排序(找前 N 个最小)
std::vector<int> data = {5, 2, 8, 1, 9, 3};
std::ranges::partial_sort(data, data.begin() + 3); // 前3个最小
for (int n : data) {
std::cout << n << " ";
}
std::cout << "\n";
// stable_sort - 稳定排序(保持相等元素的相对顺序)
#include <vector>
#include <ranges>
#include <iostream>
std::vector<int> nums = {1, 2, 3, 4, 5};
std::vector<int> result(nums.size());
// transform - 转换并写入另一个容器
std::ranges::transform(nums, result.begin(), [](int x) { return x * x; });
for (int n : result) {
std::cout << n << " "; // 1 4 9 16 25
}
std::cout << "\n";
// 两个范围的操作
std::vector<int> a = {1, 2, 3};
std::vector<int> b = {10, 20, 30};
std::vector<int> c(3);
std::ranges::transform(a, b, c.begin(), std::plus<>{});
for (int n : c) {
std::cout << n << " "; // 11 22 33
}
std::cout << "\n";

std::ranges::reduce(C++23 可用,C++17/20 用 accumulate)

Section titled “std::ranges::reduce(C++23 可用,C++17/20 用 accumulate)”
#include <vector>
#include <numeric>
#include <iostream>
#include <functional>
std::vector<int> nums = {1, 2, 3, 4, 5};
// accumulate(C++20 前的 reduce)
int sum = std::accumulate(nums.begin(), nums.end(), 0);
std::cout << "Sum: " << sum << "\n"; // 15
int product = std::accumulate(nums.begin(), nums.end(), 1, std::multiplies<>{});
std::cout << "Product: " << product << "\n"; // 120
// 使用 transform + accumulate
int sum_of_squares = std::accumulate(
nums.begin(), nums.end(), 0,
[](int acc, int x) { return acc + x * x; });
std::cout << "Sum of squares: " << sum_of_squares << "\n"; // 55
#include <vector>
#include <ranges>
#include <iostream>
#include <string>
#include <algorithm>
struct Product {
std::string name;
std::string category;
double price;
int stock;
};
std::vector<Product> inventory = {
{"Laptop", "Electronics", 999.99, 5},
{"Mouse", "Electronics", 29.99, 50},
{"Desk", "Furniture", 299.99, 10},
{"Chair", "Furniture", 199.99, 8},
{"Keyboard", "Electronics", 79.99, 30},
{"Monitor", "Electronics", 349.99, 15},
{"Book: C++", "Books", 49.99, 100},
{"Book: Python", "Books", 39.99, 80}
};
int main() {
// 管道:Electronics 类别 -> 有库存 -> 按价格排序 -> 取前3 -> 提取名称
std::cout << "=== Top 3 Affordable Electronics ===\n";
auto affordable_electronics = inventory
| std::views::filter([](const Product& p) {
return p.category == "Electronics" && p.stock > 0;
})
| std::views::transform([](const Product& p) {
return std::pair(p.name, p.price);
})
| std::views::filter([](const auto& pair) {
return pair.second < 500.0;
});
// 排序需要先收集(ranges 排序算法作用于容器)
std::vector<std::pair<std::string, double>> sorted(
affordable_electronics.begin(), affordable_electronics.end());
std::ranges::sort(sorted, {}, &std::pair<std::string, double>::second);
int count = 0;
for (const auto& [name, price] : sorted | std::views::take(3)) {
std::cout << ++count << ". " << name << " - $" << price << "\n";
}
// 计算 Electronics 总库存值
std::cout << "\n=== Electronics Inventory Value ===\n";
double total_value = 0;
for (const auto& p : inventory | std::views::filter([](const Product& p) {
return p.category == "Electronics";
})) {
total_value += p.price * p.stock;
}
std::cout << "Total value: $" << total_value << "\n";
// 最贵的商品
std::cout << "\n=== Most Expensive Items ===\n";
auto all_items = inventory
| std::views::transform([](const Product& p) {
return std::pair(p.name, p.price);
});
std::vector<std::pair<std::string, double>> items(
all_items.begin(), all_items.end());
auto max_it = std::ranges::max_element(items, {}, &std::pair<std::string, double>::second);
std::cout << "Most expensive: " << max_it->first << " - $" << max_it->second << "\n";
}
#include <vector>
#include <ranges>
#include <iostream>
std::vector<int> nums = {1, 2, 3, 4, 5};
auto view = nums
| std::views::filter([](int x) {
std::cout << "filter(" << x << ") ";
return x % 2 == 0;
})
| std::views::transform([](int x) {
std::cout << "transform(" << x << ") ";
return x * x;
});
std::cout << "View created, no computation yet.\n";
// 输出:View created, no computation yet.
// 开始迭代才真正计算
std::cout << "First element: ";
for (int n : view | std::views::take(1)) {
std::cout << "result=" << n << "\n";
}
// 输出:filter(1) filter(2) transform(2) result=4
// 只有真正需要的元素被处理
#include <vector>
#include <ranges>
#include <numeric>
// 即时求值:一次性计算所有
std::vector<int> nums = {1, 2, 3, 4, 5};
auto immediate_sum_of_squares = []() {
std::vector<int> filtered;
for (int n : nums) {
if (n % 2 == 0) filtered.push_back(n);
}
std::vector<int> transformed;
for (int n : filtered) {
transformed.push_back(n * n);
}
return std::accumulate(transformed.begin(), transformed.end(), 0);
};
// 惰性求值:按需计算
auto lazy_sum_of_squares = []() {
return std::accumulate(
nums
| std::views::filter([](int x) { return x % 2 == 0; })
| std::views::transform([](int x) { return x * x; }),
0
);
};
// lazy 版本可能只处理必需元素就停止
// (如果算法是短路的,如 find)
#include <vector>
#include <ranges>
#include <iostream>
// 视图通常不缓存结果
auto view = std::views::iota(1)
| std::views::transform([](int x) {
std::cout << "computing " << x << "\n";
return x;
});
// 第一次迭代
std::cout << "First iteration:\n";
for (int i : view | std::views::take(3)) {
std::cout << "got " << i << "\n";
}
// 第二次迭代 - 重新计算!
std::cout << "Second iteration:\n";
for (int i : view | std::views::take(3)) {
std::cout << "got " << i << "\n";
}
// 如果需要缓存,使用 ranges::to 或手动收集

Ranges vs 传统方式:

特性传统算法Ranges
求值时机即时(创建中间容器)惰性(按需计算)
组合方式函数嵌套管道 | 操作符
无限序列不支持支持
代码可读性嵌套调用线性管道

常用视图速查:

视图说明示例
filter(pred)过滤元素v | filter(odd)
transform(f)转换元素v | transform(square)
take(n)取前 n 个v | take(5)
drop(n)跳过前 n 个v | drop(3)
reverse反向v | reverse
iota(n)产生序列iota(1, 10)
all容器转视图all(vec)

管道组合原则:

  • filter 后 take - 先过滤后限制数量
  • take 后 filter - 先限制后过滤(结果可能为空)
  • transform 放最后 - 转换结果

下章预告:ch28 学习 C++20 其他现代特性,包括 consteval、std::format、std::span 和三路比较运算符。