当前位置: 首页 > news >正文

C++ 20 视图view笔记

1. C++ 的视图(View)概念

C++ 的视图(或称为“范围适配器”)是 C++20 范围库(Ranges Library)的核心组成部分。它提供了一种对序列(如容器、数组等)进行非拥有(non-owning)、延迟计算(lazy-evaluation) 的转换和组合的方式。

一个视图具有以下关键特性:

  1. 不分配(Non-owning):视图本身不持有底层序列的数据。它只是引用或“观察”已有的数据源(如 std::vector, std::list, 原始数组等)。因此,创建视图的开销通常很小。
  2. 延迟计算(Lazy Evaluation):对视图应用的转换操作(如过滤、变换)并不会立即执行。只有在真正需要计算结果(例如,开始迭代时)时,这些操作才会按需进行。
  3. 可组合(Composable):多个视图适配器可以通过管道操作符 | 连接起来,形成一个操作管道,代码非常清晰且表达力强。

2. “不分配”和“延迟计算”特性

视图的实现依赖于两个核心概念:迭代器(Iterators)惰性求值

a) 实现“不分配”

视图是一个轻量级的对象,它通常只包含:

  • 对原始数据源的引用(或指针/迭代器对)。
  • 一些必要的状态信息(例如,过滤操作的谓词函数、变换操作的函数对象等)。

因为它不包含数据本身,所以构造和复制视图的成本非常低,通常只是复制几个指针或迭代器。

示例:std::views::take

#include <ranges>
#include <vector>
#include <iostream>int main() {std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};// 创建一个 `take_view`,它只“观察”前5个元素auto first_five = numbers | std::views::take(5);// 此时,first_five 只是一个轻量级视图// 它内部可能只存储了:// - numbers.begin()// - numbers.begin() + 5// 它没有复制任何numbers的数据for (int num : first_five) { // 迭代时,它直接使用numbers的迭代器std::cout << num << ' '; // 输出:1 2 3 4 5}
}

first_five 视图没有分配任何新的内存来存储这5个数字,它只是持有了指向 numbers 起始和结束位置的迭代器。

b) 实现“延迟计算”

视图的操作(如 filter, transform)并不会立即遍历整个数据集。相反,它返回一个特殊的视图对象,这个对象重载了 begin()end() 方法。

当你开始迭代这个视图时(例如在 range-based for 循环中),它的迭代器才会在每次递增时执行必要的计算。

示例:std::views::filter + std::views::transform

#include <ranges>
#include <vector>
#include <iostream>int main() {std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};// 创建一个管道:先过滤出偶数,再将每个偶数平方auto processed_view = numbers| std::views::filter([](int n) { return n % 2 == 0; })| std::views::transform([](int n) { return n * n; });// 注意:到这里为止,没有任何计算发生!// 没有遍历,没有创建新的容器。// `processed_view` 只是一个轻量级对象,它存储了:// - 指向numbers的引用// - 两个lambda函数std::cout << "开始计算:\n";for (int result : processed_view) { // 迭代在这里开始!// 第一次迭代:从numbers.begin()开始,找到第一个偶数2,然后计算2*2=4,输出4。// 下一次迭代:继续在numbers中找下一个偶数4,然后计算4*4=16,输出16。// ... 以此类推,直到遍历结束。std::cout << result << ' '; // 输出:4 16 36 64 100}
}

延迟计算的优势:

  • 性能:如果提前退出循环(例如,找到所需元素后使用 break),可以避免不必要的计算。
    for (int result : processed_view) {if (result > 50) break; // 可能只计算了前几个元素就退出了std::cout << result << ' ';
    }
    
  • 无限序列:可以处理理论上无限的序列,因为计算是按需进行的。
    #include <ranges>
    #include <iostream>// 一个生成无限序列的视图(从0开始,每次+1)
    auto infinite_view = std::views::iota(0);// 取前10个偶数并平方
    auto result_view = infinite_view| std::views::filter([](int n) { return n % 2 == 0; })| std::views::transform([](int n) { return n * n; })| std::views::take(10); // 没有`take(10)`的话,迭代会永远进行下去for (auto num : result_view) {std::cout << num << ' '; // 输出:0 4 16 36 64 100 144 196 256 324
    }
    

3. 常见的视图适配器

C++20 标准库提供了许多有用的视图适配器:

适配器功能示例
views::filter(pred)过滤出满足谓词 pred 的元素vec | views::filter(is_even)
views::transform(fun)对每个元素应用函数 fun 进行转换vec | views::transform(square)
views::take(n)取前 n 个元素vec | views::take(5)
views::drop(n)跳过前 n 个元素vec | views::drop(5)
views::reverse反转序列vec | views::reverse
views::keys对于类似 pair 的序列,取其 first 成员map | views::keys
views::values对于类似 pair 的序列,取其 second 成员map | views::values
views::iota(start)生成一个从 start 开始逐步递增的序列views::iota(10)

4. 重要注意事项

  1. 生命周期(Lifetime):由于视图是非拥有的,你必须确保视图所引用的底层数据源的生命周期比视图更长。使用一个已经销毁的容器创建的视图会导致悬空引用和未定义行为。

    auto create_dangling_view() {std::vector<int> local_data = {1, 2, 3};return local_data | std::views::transform([](int n) { return n * 2; });// 错误!返回的视图引用了已经销毁的local_data
    }
    
  2. 求值次数(Evaluation Count):在延迟计算的管道中,谓词或转换函数可能会被调用多次。例如,在 filtertransform 的组合中,filter 的谓词可能会对每个元素判断多次(取决于实现细节)。因此,确保这些函数是无状态幂等的(多次调用产生相同结果),并且没有副作用。

总结

C++ 的视图机制通过非拥有的轻量级包装器和延迟迭代策略,完美实现了不分配内存延迟计算。它提供了一种声明式、高性能的方式来处理和转换数据序列,是现代 C++ 函数式编程风格的重要基石。在使用时,务必注意底层数据的生命周期问题。


文章转载自:

http://0THbEDlT.sftpg.cn
http://I2NJ2kVz.sftpg.cn
http://vaijxYI0.sftpg.cn
http://UajWBbIP.sftpg.cn
http://c8hfQREt.sftpg.cn
http://WGoAAcLT.sftpg.cn
http://2nGsZ9rD.sftpg.cn
http://IO0373dA.sftpg.cn
http://feOzpbNW.sftpg.cn
http://gaXRG8D9.sftpg.cn
http://lyJaw7gr.sftpg.cn
http://z7bOL5rh.sftpg.cn
http://NzjAX6di.sftpg.cn
http://4pWxSJmq.sftpg.cn
http://3zIZtK22.sftpg.cn
http://fxo0pwWE.sftpg.cn
http://dRsojTIu.sftpg.cn
http://iQBeSVTG.sftpg.cn
http://mXCXWYJM.sftpg.cn
http://11Nq5vnH.sftpg.cn
http://hwxaFooJ.sftpg.cn
http://xG8Z3Pm0.sftpg.cn
http://aXpqIyBN.sftpg.cn
http://8osBUDwS.sftpg.cn
http://ud75b6Z9.sftpg.cn
http://Ehwj5DCX.sftpg.cn
http://HHVcQ3ZB.sftpg.cn
http://DMGsHWhr.sftpg.cn
http://w5thKbNK.sftpg.cn
http://u3G5uPKu.sftpg.cn
http://www.dtcms.com/a/375248.html

相关文章:

  • HTML 网页静态托管 API 接口文档(可集成到智能体Agent)
  • 在uni-app中使用lottie-web来展示Lottie动画
  • Python数据可视化基础:使用Matplotlib绘制图表
  • CodeBuddy Code深度实战:从零构建智能电商推荐系统的完整开发历程
  • 【Kubernetes知识点】PriorityClass,HPA和CICD
  • 赋能多场景创新:明远智睿H618核心板
  • (C++)数据结构初阶(顺序表的实现)
  • 一手实测,文心x1.1的升级很惊喜啊
  • 【系统分析师】第18章-关键技术:移动应用系统分析与设计(核心总结)
  • echarts 实现柱状图自动滚动展示数据(Vue3)
  • 基于Python的购物商城网站电商管理系统【2026最新】
  • Electron 分发策略:创建安装程序与自动更新
  • IAR 集成开发环境入门指南:字体设置与调试实战
  • CentOS7下Ceph集群部署实战
  • 逆元,除法同余,容斥原理笔记
  • 【springboot+vue】党员党建活动管理平台(源码+文档+调试+基础修改+答疑)
  • JAVA 面试 MySQL
  • 【Pandas】3.2-数据预处理:行的基本操作
  • 【展厅多媒体】 AI人工智能赋能虚拟数字展厅应用与制作
  • Python入门教程之逻辑运算符
  • 构建AI智能体:二十八、大语言模型BERT:原理、应用结合日常场景实践全面解析
  • pytest并发测试,资源问题导致用例失败解决办法
  • 【openEuler 24.03 LTS SP2】真实实验部署ollama0.11.6+deepseekR1:1.5b+open-webUI
  • 欢迎来到“个人产品化”时代
  • 【论文阅读】REFRAG:一个提升RAG解码效率的新思路
  • 云原生监控系统 Prometheus大总结 20250909
  • Python解释器安装配置教程(Windows)
  • Java爬虫获取京东item_get_app数据的实战指南
  • HashMap(JDK1.7到1.8的过渡)
  • 趣味学RUST基础篇(函数式编程迭代器)