C++20 Ranges
C++20 引入了
1.基本概念
1.1 Range
- 定义:任何提供迭代器对
[begin, end)的对象 - 类型:
- 标准容器:
vector,list,array等 - 原生数组:
int arr[10] - 特殊范围:
istream_view(从流读取),iota_view(生成序列)
- 标准容器:
- 关键特征:统一迭代接口,消除容器差异
// Range 概念的基本要求
template<typename T>
concept Range = requires(T& t) {
std::ranges::begin(t); // 必须有 begin()
std::ranges::end(t); // 必须有 end()
};
#include <ranges>
#include <vector>
#include <string>
#include <array>
// 验证各种类型是否满足 Range 概念
static_assert(std::ranges::range<std::vector<int>>); // true
static_assert(std::ranges::range<std::string>); // true
static_assert(std::ranges::range<std::array<int, 5>>); // true
static_assert(std::ranges::range<int[10]>); // true
1.2 View
- 本质:轻量级范围包装器(零拷贝),是轻量级、非拥有的范围,支持常数时间复制、移动和赋值
- 特性:
- 惰性求值(操作延迟到迭代时)
- 时间复杂度 O(1) 构造/析构
- 可组合性(通过
|管道符连接)
- 所有权:不拥有数据,仅引用底层范围
// View 概念的要求
template<typename T>
concept View = std::ranges::range<T> &&
std::movable<T> &&
std::ranges::view_base<T>;
#include <ranges>
#include <vector>
// 验证各种视图类型
static_assert(std::ranges::view<std::ranges::iota_view<int, int>>); // true
static_assert(std::ranges::view<decltype(std::views::iota(0) | std::views::take(5))>); // true
// 容器不是 View(因为复制成本高)
static_assert(!std::ranges::view<std::vector<int>>); // false
1.3 范围适配器 (Range Adapters)
范围适配器是创建新视图的函数对象:
- 作用:生成视图的工厂函数
- 命名空间:
std::views - 分类(部分):
// 元素操作
transform(fn) // 映射元素
filter(pred) // 条件过滤
// 结构操作
take(n) // 取前n元素
drop(n) // 跳过前n元素
reverse // 逆序
keys/values // 键/值提取(针对pair-like)
// 生成操作
iota(start) // 无限序列生成
empty<T> // 空范围
#include <ranges>
#include <vector>
#include <iostream>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 适配器可以存储为对象
auto filter_even = std::views::filter([](int n) { return n % 2 == 0; });
auto take_three = std::views::take(3);
auto square = std::views::transform([](int n) { return n * n; });
// 组合使用
auto result = numbers | filter_even | take_three | square;
for (int n : result) {
std::cout << n << " "; // 输出: 4 16 36
}
return 0;
}
2. 主要功能
2.1 范围工厂 (Range Factories)
创建新的范围或视图:
#include <ranges>
#include <iostream>
int main() {
// iota_view: 生成连续序列
auto ints = std::views::iota(0, 5); // [0, 5)
for (int i : ints) {
std::cout << i << " "; // 0 1 2 3 4
}
std::cout << "\n";
// empty_view: 空范围
auto empty = std::views::empty<int>;
std::cout << "Empty size: " << std::ranges::size(empty) << "\n"; // 0
// single_view: 单元素范围
auto single = std::views::single(42);
for (int i : single) {
std::cout << i << " "; // 42
}
std::cout << "\n";
// iota 无限序列
auto infinite = std::views::iota(10);
auto first_five = infinite | std::views::take(5);
for (int i : first_five) {
std::cout << i << " "; // 10 11 12 13 14
}
return 0;
}
2.2 范围适配器 (Range Adapters)
修改现有范围:
#include <ranges>
#include <vector>
#include <iostream>
#include <string>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// filter: 过滤元素
auto evens = numbers | std::views::filter([](int n) { return n % 2 == 0; });
std::cout << "Evens: ";
for (int n : evens) std::cout << n << " "; // 2 4 6 8 10
std::cout << "\n";
// transform: 转换元素
auto squares = numbers | std::views::transform([](int n) { return n * n; });
std::cout << "Squares: ";
for (int n : squares) std::cout << n << " "; // 1 4 9 16 25 36 49 64 81 100
std::cout << "\n";
// take: 取前N个元素
auto first_three = numbers | std::views::take(3);
std::cout << "First three: ";
for (int n : first_three) std::cout << n << " "; // 1 2 3
std::cout << "\n";
// drop: 跳过前N个元素
auto skip_two = numbers | std::views::drop(2);
std::cout << "Skip two: ";
for (int n : skip_two) std::cout << n << " "; // 3 4 5 6 7 8 9 10
std::cout << "\n";
// take_while: 取满足条件的前缀
// 一旦遇到不满足条件的元素就停止,结果只包含范围开始的连续元素
auto prefix_less_than_5 = numbers | std::views::take_while([](int n) { return n < 5; });
std::cout << "Prefix < 5: ";
for (int n : prefix_less_than_5) std::cout << n << " "; // 1 2 3 4
std::cout << "\n";
// drop_while: 跳过满足条件的前缀
// 跳过范围开始处连续满足条件的元素,保留剩余部分(遇到第一个不满足条件的元素后,保留该元素及其后面的所有元素)
std::vector<int> sorted_nums = {1, 2, 3, 4, 5, 10, 15};
auto skip_less_than_5 = sorted_nums | std::views::drop_while([](int n) { return n < 5; });
std::cout << "Skip < 5: ";
for (int n : skip_less_than_5) std::cout << n << " "; // 5 10 15
std::cout << "\n";
// reverse: 反转序列
auto reversed = numbers | std::views::reverse;
std::cout << "Reversed: ";
for (int n : reversed) std::cout << n << " "; // 10 9 8 7 6 5 4 3 2 1
std::cout << "\n";
return 0;
}
2.3 范围算法 (Range Algorithms)
直接作用于范围的算法:
#include <ranges>
#include <vector>
#include <iostream>
#include <algorithm>
int main() {
std::vector<int> numbers = {5, 2, 8, 1, 9, 3};
// sort: 排序
std::ranges::sort(numbers);
std::cout << "Sorted: ";
for (int n : numbers) std::cout << n << " "; // 1 2 3 5 8 9
std::cout << "\n";
// find: 查找
auto it = std::ranges::find(numbers, 5);
if (it != numbers.end()) {
std::cout << "Found 5 at position: " << (it - numbers.begin()) << "\n";
}
// all_of, any_of, none_of
/**
** std::ranges::all_of
** 作用:检查范围中所有元素是否都满足条件
** 返回值:如果所有元素都满足条件返回true,否则返回false
** 短路:遇到第一个不满足条件的元素时立即返回false
**
** std::ranges::any_of
** 作用:检查范围中至少有一个元素满足条件
** 返回值:如果有至少一个元素满足条件返回true,否则返回false
** 短路:遇到第一个满足条件的元素时立即返回true
**
** std::ranges::none_of
** 作用:检查范围中没有元素满足条件
** 返回值:如果没有元素满足条件返回true,否则返回false
** 短路:遇到第一个满足条件的元素时立即返回false
**/
bool all_positive = std::ranges::all_of(numbers, [](int n) { return n > 0; });
bool has_even = std::ranges::any_of(numbers, [](int n) { return n % 2 == 0; });
std::cout << "All positive: " << all_positive << ", Has even: " << has_even << "\n";
// count, count_if
/**
** std::ranges::count
** 作用:统计范围中等于指定值的元素个数
** 参数:范围和要查找的值
** 返回值:满足条件的元素数量(range_difference_t类型)
**
** std::ranges::count_if
** 作用:统计范围中满足谓词条件的元素个数
** 参数:范围和谓词函数
** 返回值:满足条件的元素数量(range_difference_t类型)
**/
long long count_even = std::ranges::count_if(numbers, [](int n) { return n % 2 == 0; });
std::cout << "Even count: " << count_even << "\n"; // 2
return 0;
}
3. 性能分析
3.1 惰性求值 (Lazy Evaluation)
#include <ranges>
#include <vector>
#include <iostream>
#include <numeric>
void lazy_evaluation()
{
int filter_count = 0;
std::vector<int> large_data(1000000);
std::iota(large_data.begin(), large_data.end(), 1);
std::cout << "Before creating a view,filter_count = " << filter_count << "\n";
// 创建视图
// 这行代码只是创建了一个视图,不会执行任何计算
auto result = large_data
| std::views::filter([&filter_count](int n) {
filter_count++;
std::cout << "n: " << n << "\n";
return n % 2 == 0;
})
| std::views::take(10); // 只取10个
std::cout << "After creating the view,filter_count = " << filter_count << "\n";
std::cout << "Start the traversal:\n";
int output_count = 0;
// 只有在这里遍历时才会执行计算
for (const auto n : result) {
std::cout << "result: " << n << "\n";
output_count++;
}
std::cout << "After the traversal is completed,filter_count = " << filter_count << "\n";
std::cout << "After the traversal is completed,output_count = " << output_count << "\n";
}
3.2 内存效率对比
#include <ranges>
#include <vector>
#include <algorithm>
#include <iostream>
// 使用传统STL算法方式 - 创建中间容器
void traditional_approach()
{
std::vector<int> data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 每个操作都创建新容器
std::vector<int> evens;
std::copy_if(data.begin(), data.end(), std::back_inserter(evens),
[](int n) { return n % 2 == 0; });
std::vector<int> squared;
std::transform(evens.begin(), evens.end(), std::back_inserter(squared),
[](int n) { return n * n; });
// 最终结果只取前3个
squared.resize(3);
// 创建了多个中间容器,浪费内存。
// 同时如果要想效率高,就不能直接使用std::copy_if和std::transform算法来做这三部操作。得自己手动for循环遍历取数据并判断提前退出
}
// Ranges 方式 - 无中间容器
void ranges_approach()
{
std::vector<int> data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 管道操作,无中间容器
auto result = data
| std::views::filter([](int n) { return n % 2 == 0; })
| std::views::transform([](int n) { return n * n; })
| std::views::take(3);
// 没有创建中间容器,内存效率比直接使用std::copy_if和std::transform更高。同时也更方便,不需要自己写for循环遍历做逻辑操作
for (int n : result) {
std::cout << n << " ";
}
}
3.3 编译时优化
#include <ranges>
#include <vector>
#include <iostream>
// 编译器可以进行优化
void compiler_optimization()
{
std::vector<int> data(1000);
std::iota(data.begin(), data.end(), 1);
// 复杂的管道操作
auto pipeline = data
| std::views::filter([](int n) { return n > 500; })
| std::views::transform([](int n) { return n * 2; })
| std::views::filter([](int n) { return n % 3 == 0; })
| std::views::take(10);
// 编译器可以将这些操作融合优化
for (int n : pipeline) {
if (n > 2000) break; // 甚至可以进一步优化
std::cout << n << " ";
}
}
4.优点与缺点
4.1 优点
4.1.1 代码可读性
// 传统方式
std::vector<int> data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
std::vector<int> temp1, temp2, result;
std::copy_if(data.begin(), data.end(), std::back_inserter(temp1),
[](int n) { return n % 2 == 0; });
std::transform(temp1.begin(), temp1.end(), std::back_inserter(temp2),
[](int n) { return n * n; });
std::copy(temp2.begin(), temp2.begin() + std::min(3, (int)temp2.size()),
std::back_inserter(result));
// Ranges 方式
auto result = data
| std::views::filter([](int n) { return n % 2 == 0; })
| std::views::transform([](int n) { return n * n; })
| std::views::take(3);
4.1.2 类型安全
#include <ranges>
#include <vector>
#include <concepts>
// 编译时检查
template<std::ranges::random_access_range R>
void process_random_access(R&& range) {
// 只接受随机访问范围
}
template<std::ranges::input_range R>
void process_input(R&& range) {
// 接受任何输入范围
}
void type_safety_demo() {
std::vector<int> vec = {1, 2, 3};
std::list<int> lst = {1, 2, 3};
process_random_access(vec); // OK
// process_random_access(lst); // 编译错误 - list 不是随机访问
process_input(vec); // OK
process_input(lst); // OK
}
4.1.3 某些特定复杂场景下的性能优势
#include <algorithm>
#include <iostream>
#include <ranges>
#include <vector>
#include <chrono>
#include <numeric>
void performance_comparison() {
constexpr size_t N = 10000000;
std::vector<int> data(N);
std::iota(data.begin(), data.end(), 1);
// 测试传统方式 - 需要分别计算多个相关结果
auto start = std::chrono::high_resolution_clock::now();
// 计算结果1: 偶数的平方,取前1000个
std::vector<int> result1;
result1.reserve(1000);
for (const auto& n : data) {
if (n % 2 == 0) {
result1.emplace_back(n * n);
if (result1.size() == 1000) break;
}
}
// 计算结果2: 偶数的平方中个位数为6的数,取前1000个
std::vector<int> result2;
result2.reserve(1000);
for (const auto& n : data) {
if (n % 2 == 0) {
auto squared = n * n;
if (squared % 10 == 6) {
result2.emplace_back(squared);
if (result2.size() == 1000) break;
}
}
}
// 计算结果3: 偶数的平方中个位数为4的数,取前1000个
std::vector<int> result3;
result3.reserve(1000);
for (const auto& n : data) {
if (n % 2 == 0) {
auto squared = n * n;
if (squared % 10 == 4) {
result3.emplace_back(squared);
if (result3.size() == 1000) break;
}
}
}
auto end = std::chrono::high_resolution_clock::now();
auto traditional_time = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
// 测试 Ranges 方式 - 可以复用中间步骤
start = std::chrono::high_resolution_clock::now();
// 创建一次处理管道,多次复用
auto even_squared = data
| std::views::filter([](int n) { return n % 2 == 0; })
| std::views::transform([](int n) { return n * n; });
// 复用中间结果计算不同目标
auto result1_ranges = even_squared | std::views::take(1000);
auto result2_ranges = even_squared
| std::views::filter([](int n) { return n % 10 == 6; })
| std::views::take(1000);
auto result3_ranges = even_squared
| std::views::filter([](int n) { return n % 10 == 4; })
| std::views::take(1000);
std::vector<int> r1, r2, r3;
r1.reserve(1000); r2.reserve(1000); r3.reserve(1000);
std::ranges::copy(result1_ranges, std::back_inserter(r1));
std::ranges::copy(result2_ranges, std::back_inserter(r2));
std::ranges::copy(result3_ranges, std::back_inserter(r3));
end = std::chrono::high_resolution_clock::now();
auto ranges_time = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << "Traditional time: " << traditional_time.count() << " μs\n";
std::cout << "Ranges time: " << ranges_time.count() << " μs\n";
}
总结优势就是:
- 组合性和复用性 - 可以轻松组合和复用处理步骤
- 代码清晰度 - 更容易表达复杂的数据处理管道
- 延迟计算 - 只在需要时才进行计算
- 函数式编程风格 - 更容易进行并行化等优化
4.2 缺点
4.2.1 编译时间增加
// 复杂的模板实例化会增加编译时间
auto complex_pipeline = data
| std::views::filter([](const auto& x) { return x.value > 0; })
| std::views::transform([](const auto& x) { return x.value * 2; })
| std::views::filter([](int x) { return x < 100; })
| std::views::transform([](int x) { return std::to_string(x); })
| std::views::take(50)
| std::views::drop(10);
4.2.2 调试困难
// 复杂的管道表达式在调试器中难以跟踪
auto problematic_pipeline = data
| std::views::filter(some_complex_predicate)
| std::views::transform(some_complex_transformation)
| std::views::filter(another_predicate)
| std::views::take(some_dynamic_value);
// 当出现问题时,很难确定是哪一步出错了
4.2.3 学习曲线
// 新概念需要时间掌握
auto beginner_unfriendly = data
| std::views::filter([](auto&& x) {
return std::invoke(&SomeClass::is_valid, x) &&
x.get_value() > some_threshold;
})
| std::views::transform([](auto&& x) {
return std::make_pair(x.get_id(), process(x));
})
| std::views::chunk(10)
| std::views::join
| std::views::take_while([](const auto& pair) {
return pair.first != sentinel_value;
});
5. 高级使用技巧
5.1 自定义范围适配器
#include <ranges>
#include <vector>
#include <iostream>
// 自定义适配器:只取质数
auto primes_only = std::views::filter([](int n) {
if (n < 2) return false;
for (int i = 2; i * i <= n; ++i) {
if (n % i == 0) return false;
}
return true;
});
int main() {
auto numbers = std::views::iota(1, 30);
auto primes = numbers | primes_only;
std::cout << "Primes: ";
for (int p : primes) {
std::cout << p << " "; // 2 3 5 7 11 13 17 19 23 29
}
return 0;
}
5.2 范围组合模式
#include <ranges>
#include <vector>
#include <iostream>
// 可重用的管道组合
auto data_processing_pipeline = [](auto&& range) {
return range
| std::views::filter([](const auto& item) { return item.active; })
| std::views::transform([](const auto& item) { return item.value; })
| std::views::filter([](int val) { return val > 0; })
| std::views::take(100);
};
struct DataItem {
int value;
bool active;
};
int main() {
std::vector<DataItem> items(1000);
// 初始化数据...
auto processed = items | data_processing_pipeline;
for (int val : processed) {
std::cout << val << " ";
}
return 0;
}
5.3 与现有代码集成
#include <ranges>
#include <vector>
#include <algorithm>
// 将范围结果转换为传统容器
template<typename Range>
auto to_vector(Range&& range) {
using ValueType = std::ranges::range_value_t<Range>;
std::vector<ValueType> result;
std::ranges::copy(range, std::back_inserter(result));
return result;
}
int main() {
std::vector<int> data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 使用范围操作
auto processed = data
| std::views::filter([](int n) { return n % 2 == 0; })
| std::views::transform([](int n) { return n * n; })
| std::views::take(3);
// 转换为传统 vector 以与旧代码兼容
auto result_vector = to_vector(processed);
// 现在可以使用传统算法
std::sort(result_vector.begin(), result_vector.end());
return 0;
}
6. 实际应用场景
6.1 数据处理管道
#include <ranges>
#include <vector>
#include <string>
#include <iostream>
struct Employee {
std::string name;
int age;
double salary;
std::string department;
};
void employee_analysis() {
std::vector<Employee> employees = {
{"Alice", 30, 75000, "Engineering"},
{"Bob", 25, 65000, "Marketing"},
{"Charlie", 35, 85000, "Engineering"},
{"Diana", 28, 70000, "HR"},
{"Eve", 32, 90000, "Engineering"}
};
// 高薪工程师的姓名
auto high_paid_engineers = employees
| std::views::filter([](const Employee& e) {
return e.department == "Engineering" && e.salary > 70000;
})
| std::views::transform([](const Employee& e) {
return e.name;
});
std::cout << "High-paid engineers: ";
for (const auto& name : high_paid_engineers) {
std::cout << name << " ";
}
// 输出: Charlie Eve
}
6.2 文本处理
#include <ranges>
#include <string>
#include <iostream>
#include <sstream>
void text_processing() {
std::string text = "The quick brown fox jumps over the lazy dog";
// 分词并处理
auto words = text
| std::views::split(' ')
| std::views::transform([](auto&& word) {
return std::string_view(&*word.begin(), std::ranges::distance(word));
})
| std::views::filter([](const std::string_view& word) {
return word.length() > 3;
})
| std::views::transform([](const std::string_view& word) {
std::string upper(word);
std::transform(upper.begin(), upper.end(), upper.begin(), ::toupper);
return upper;
});
std::cout << "Long words in uppercase: ";
for (const auto& word : words) {
std::cout << word << " ";
}
// 输出: QUICK BROWN JUMPS OVER LAZY
}
7. 典型陷阱
7.1 迭代器失效
vector<int> data{1,2,3};
auto view = data | views::filter([](int x) { return x > 1; });
for (auto it = view.begin(); it != view.end(); ) {
if (*it == 2) {
data.push_back(4); // 导致迭代器失效!
}
++it;
}
7.2 谓词副作用
int counter = 0;
auto bad_view = data | views::transform([&](int x) {
return x + counter++; // 结果依赖执行顺序
});
7.3 性能反模式
多次遍历导致重复计算
void multiple_traversal_trap()
{
std::vector<int> data = {1, 2, 3, 4, 5};
auto expensive_view = data | std::views::filter([](int n) {
// 模拟昂贵的计算
std::this_thread::sleep_for(std::chrono::milliseconds(100));
return n > 2;
});
// 第一次遍历:执行计算
std::cout << "First traversal:\n";
for (int n : expensive_view) {
std::cout << n << " ";
}
// 第二次遍历:重新执行相同的计算!
std::cout << "\nSecond traversal:\n";
for (int n : expensive_view) {
std::cout << n << " ";
}
// 总耗时是单次的两倍!
}
如果需要多次使用结果,考虑转换为容器.
// 避免不必要的惰性求值
void avoid_unnecessary_lazy()
{
std::vector<int> data = {1, 2, 3, 4, 5};
// 如果需要多次使用结果,考虑转换为容器
auto view = data | std::views::filter([](int n) {
// 模拟昂贵的计算
std::this_thread::sleep_for(std::chrono::milliseconds(100));
return n > 2;
});
std::vector<int> materialized(view.begin(), view.end());
// 现在可以多次使用materialized而无需重复计算
}
7.4 生命周期
7.4.1 临时对象生命周期问题
std::string get_string()
{
return "Hello World";
}
void lifetime_trap()
{
// 危险!临时对象在表达式结束后销毁
// auto view = get_string() | std::views::split(' '); // 编译错误或运行时错误
// 正确做法:先存储再使用
auto str = get_string();
auto view = str | std::views::split(' ');
// 或者使用 std::string_view
std::string_view sv = "Hello World";
auto view2 = sv | std::views::split(' '); // 安全
}
7.4.2 引用悬空
void dangling_reference_trap()
{
auto create_view() {
std::vector<int> temp = {1, 2, 3, 4, 5};
// 危险!返回引用已销毁的临时对象
return temp | std::views::filter([](int n) { return n % 2 == 0; });
}
// auto view = create_view(); // 悬空引用!
// 正确做法:返回容器而不是视图
auto create_container() {
std::vector<int> temp = {1, 2, 3, 4, 5};
auto view = temp | std::views::filter([](int n) { return n % 2 == 0; });
return std::vector<int>(view.begin(), view.end()); // 转换为实际容器
}
}
7.5 类型陷阱
7.5.1 视图类型复杂且难以推断
void type_complexity_trap()
{
std::vector<int> data = {1, 2, 3, 4, 5};
// 复杂的类型,难以手动声明
auto complex_view = data
| std::views::filter([](int n) { return n % 2 == 0; })
| std::views::transform([](int n) { return n * n; })
| std::views::take(3);
// 错误:无法直接声明类型
// decltype(complex_view)::value_type val; // 复杂且不直观
// 正确:使用 auto 或模板
for (const auto& item : complex_view) {
// 处理 item
}
}
7.5.2 窄化转换问题
void narrowing_conversion_trap()
{
std::vector<int> data(1000000);
// 问题:count_if 返回 range_difference_t (通常是 long long)
// auto count = std::ranges::count_if(data, [](int n) { return n % 2 == 0; });
// Clang-Tidy 警告:从 long long 窄化转换为 int
// int count = std::ranges::count_if(data, [](int n) { return n % 2 == 0; });
// 正确做法:使用正确的类型
std::ptrdiff_t count = std::ranges::count_if(data, [](int n) { return n % 2 == 0; });
// 或者使用 auto
auto count2 = std::ranges::count_if(data, [](int n) { return n % 2 == 0; });
}
7.6 调试陷阱
调试困难
void debugging_trap()
{
std::vector<int> data(1000);
std::iota(data.begin(), data.end(), 1);
// 复杂的链式表达式在调试器中难以跟踪
auto problematic_pipeline = data
| std::views::filter([](int n) {
return n > 500 && n % 7 == 0;
})
| std::views::transform([](int n) {
return n * 2;
})
| std::views::filter([](int n) {
return n % 3 == 0;
})
| std::views::take(50);
// 当出现问题时,很难确定是哪一步出错了
for (int n : problematic_pipeline) {
if (n < 0) {
// 在这里中断调试时,很难回溯到具体是哪一步产生了错误值
}
}
// 更好的方式:分步调试
auto step1 = data | std::views::filter([](int n) { return n > 500 && n % 7 == 0; });
auto step2 = step1 | std::views::transform([](int n) { return n * 2; });
auto step3 = step2 | std::views::filter([](int n) { return n % 3 == 0; });
auto step4 = step3 | std::views::take(50);
}
总结
C++ Ranges 通过四大革新重塑序列处理:
- 声明式编程:接近自然语言的管道操作 data | filter | transform
- 零开销抽象:编译后性能等同最优手写代码
- 惰性求值:消除中间存储,支持无限序列
- 强类型安全:概念约束在编译期捕获错误
适用场景:
- 数据转换管道(ETL、数据清洗)
- 流式处理(网络数据、日志分析)
- 算法密集型应用(数值计算、机器学习)
- 替代传统STL算法/迭代器代码
未来演进:
- C++23:zip_view, join_with, chunk_by
- C++26:模式匹配集成、异步范围
C++20 Ranges 提供了现代、高效、表达力强的范围操作方式,虽然有一些缺点,但在大多数情况下其优势远大于劣势,是现代C++编程的重要工具

浙公网安备 33010602011771号