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

C++ 中 Views 的详细讲解

C++ 中的 “views” 主要指 C++20 标准引入的 Ranges 库(<ranges> 头文件)中的视图(views)组件。Ranges 库是标准模板库(STL)的扩展,旨在提供更现代、函数式风格的序列处理方式。Views 是 Ranges 库的核心部分,是一种轻量级的、非拥有的范围(range)抽象,用于对数据序列进行惰性(lazy)操作和转换,而不修改底层数据。它们基于 Eric Niebler 的 Range-v3 库设计,强调可组合性和表达力。

以下是对 C++ views 的详细讲解,包括其概念、设计原则、常见视图类型、使用方式、优势、注意事项以及示例。讲解基于 C++20 标准(以及后续小更新,如 C++23 的扩展),假设你有基本的 C++ 知识(如容器、迭代器和 lambda)。

1. Views 的核心概念
  • 什么是 View?
    View 是一个特殊的范围(range),它不存储数据,而是提供对底层范围的“视图”或“窗口”。它类似于一个过滤器或转换器,允许你以惰性方式访问或修改数据的外观,而不复制或修改原始数据。

    • 正式定义:在 C++ 标准中,view 满足 std::ranges::view 概念(concept),这是一个编译期约束。它必须是可移动的(movable)、默认可构造的,并且支持 begin/end 迭代器。
    • 关键特性
      • 惰性求值(Lazy Evaluation):View 不立即计算结果,只有在迭代或访问元素时才执行操作。这节省了内存和计算资源,尤其适合大数据处理。
      • 非拥有(Non-Owning):View 不拥有数据,仅引用底层范围。如果底层数据被销毁,view 会失效(悬垂引用问题)。
      • 轻量级:View 通常是小对象(sizeof 很小),易于复制和传递。
      • 可组合(Composable):通过管道操作符 |,可以链式组合多个 view,形成复杂的处理管道。
  • View 与 Range 的关系

    • 所有 view 都是 range,但不是所有 range 都是 view。Range 是更广义的概念(任何有 begin/end 的序列),而 view 是 range 的子集,强调惰性和视图语义。
    • 示例:std::vector 是一个 range(拥有数据),但不是 view;std::ranges::views::filter 返回的是 view。
  • 命名空间
    Views 位于 std::ranges::views 命名空间中,通常简写为 std::views(通过 using 声明)。在代码中常用 auto 推导类型,因为 view 类型复杂。

2. Views 的设计原则和优势
  • 设计原则

    • 函数式编程风格:Views 鼓励不可变操作和纯函数,类似于 Haskell 或 Rust 的迭代器链。
    • 与迭代器的兼容:Views 构建在迭代器之上,但隐藏了底层细节,提供更高级的抽象。
    • 概念驱动:使用 C++20 的概念(如 std::ranges::viewable_range)确保类型安全和编译期错误检查。
    • 管道语法| 操作符重载为视图适配器(adaptor),允许链式调用,如 range | view1 | view2
  • 优势

    1. 简洁性和可读性:取代了繁琐的迭代器操作,使代码更像自然语言描述。
    2. 性能优化:惰性求值避免不必要的计算;许多 view 是零开销抽象(zero-cost abstraction)。
    3. 安全性:非拥有的设计减少了数据复制,但需注意生命周期。
    4. 可扩展性:用户可以自定义 view 适配器。
    5. 与算法集成:Views 可以直接传递给 std::ranges 算法,如 std::ranges::sortstd::ranges::for_each
  • 缺点

    • 学习曲线:初学者可能不熟悉函数式风格。
    • 调试复杂:惰性求值使错误在运行时才暴露。
    • C++23 扩展:C++23 添加了更多 view,如 std::views::chunk,进一步增强功能。

3. 常见 Views 类型

C++20 提供了丰富的内置视图,位于 std::views。以下按功能分类列出常见视图(非 exhaustive),包括参数和用法:

  • 基本视图

    • std::views::all(range):将任意 range 转换为 view。如果 range 已满足 view 概念,直接返回;否则创建引用视图。
      • 用法:用于统一处理容器和视图。
    • std::views::empty<T>:创建一个空视图(无元素)。
      • 用法:作为占位符或测试。
  • 生成视图

    • std::views::iota(start, end):生成从 start 到 end 的连续值序列(惰性生成)。
      • 参数:start(起始值),end(可选,结束值)。
      • 示例:生成 1 到 10 的整数。
    • std::views::single(value):创建一个只包含单个元素的视图。
    • std::views::repeat(value, count)(C++23):重复生成 value,count 次。
  • 过滤和选择视图

    • std::views::filter(predicate):过滤满足谓词(predicate)的元素。
      • 参数:lambda 或函数,返回 bool。
    • std::views::take(n):取前 n 个元素。
    • std::views::take_while(predicate):取元素直到谓词为 false。
    • std::views::drop(n):跳过前 n 个元素。
    • std::views::drop_while(predicate):跳过元素直到谓词为 false。
  • 转换视图

    • std::views::transform(func):对每个元素应用 func 转换。
      • 参数:lambda 或函数,返回转换后的值。
    • std::views::elements<I>(range):从 tuple-like 元素中提取第 I 个成员。
      • 示例:从 std::vector<std::pair<int, std::string>> 提取键。
    • std::views::keys(range) / std::views::values(range):提取 map 或 pair 的键/值。
  • 结构视图

    • std::views::reverse(range):反转范围。
    • std::views::split(delimiter):按分隔符拆分范围(返回子范围视图)。
      • 参数:单个元素或子范围作为分隔符。
    • std::views::join(range_of_ranges):将范围的范围(range of ranges)扁平化连接。
    • std::views::chunk(n)(C++23):将范围分成大小为 n 的块。
  • 其他视图

    • std::views::common(range):将范围转换为使用共同迭代器(common iterator),便于与传统 STL 算法兼容。
    • std::views::as_rvalue(range)(C++23):将范围转换为右值引用视图。
    • std::views::zip(range1, range2, ...)(C++23):并行迭代多个范围,返回 tuple。

这些视图都是函数对象(adaptor),可以作为 lambda 参数或通过 | 使用。

4. 如何使用 Views
  • 基本用法
    Views 通过管道 | 组合,或作为函数调用。
    示例:

    #include <ranges>  // 包含 <vector> 和 <iostream> 等
    #include <vector>
    #include <iostream>int main() {std::vector<int> v = {1, 2, 3, 4, 5};// 管道组合视图auto even_squares = v | std::views::filter([](int x) { return x % 2 == 0; })| std::views::transform([](int x) { return x * x; });for (int x : even_squares) {std::cout << x << " ";  // 输出: 4 16}
    }
    
  • 与算法结合
    Views 可以直接传入 Ranges 算法。
    示例:

    std::vector<int> v = {3, 1, 4, 1, 5};
    auto view = std::views::take(v, 3);  // 取前3: {3,1,4}
    std::ranges::sort(view);  // 排序视图,影响底层 v: {1,1,3,4,5}
    
  • 自定义 View
    你可以定义自己的 view 适配器,使用 std::ranges::range_adaptor_closure
    示例(简单倍增视图):

    auto multiply_by_two = std::views::transform([](int x) { return x * 2; });
    auto result = v | multiply_by_two;
    
  • 生命周期管理
    Views 引用底层数据,确保底层范围在 view 使用期间有效。
    错误示例(悬垂视图):

    auto get_view() {std::vector<int> temp = {1, 2, 3};return temp | std::views::filter([](int x) { return x > 1; });  // 错误: temp 销毁后视图失效
    }
    
  • 转换为容器
    使用 std::ranges::to(C++23)将视图物化为容器:

    auto vec = view | std::ranges::to<std::vector>();
    
5. 高级示例
  • 处理字符串拆分

    #include <ranges>
    #include <string>
    #include <iostream>int main() {std::string s = "apple,banana,cherry";auto words = s | std::views::split(',');for (auto word : words) {// word 是 subrange,需要转换为 string_view 输出std::cout << std::string_view(word.begin(), word.end()) << '\n';// 输出: apple \n banana \n cherry}
    }
    
  • 生成无限序列(有限取用)

    auto positives = std::views::iota(1);  // 无限正整数: 1,2,3,...
    auto first_ten = positives | std::views::take(10);
    for (int x : first_ten) { std::cout << x << " "; }  // 1 2 3 ... 10
    
  • 处理结构化数据

    std::vector<std::pair<std::string, int>> fruits = {{"apple", 5}, {"banana", 3}};
    auto names = fruits | std::views::keys;  // 提取键: "apple", "banana"
    
  • C++23 Zip 示例

    std::vector<int> a = {1, 2, 3};
    std::vector<char> b = {'a', 'b', 'c'};
    auto zipped = std::views::zip(a, b);  // 返回 tuples: (1,'a'), (2,'b'), (3,'c')
    
6. 注意事项和最佳实践
  • 编译要求:需要 C++20+ 支持。使用 -std=c++20 编译。
  • 性能:Views 通常高效,但链式过多可能导致模板膨胀(编译慢)。在热点代码中测试。
  • 调试:使用 std::ranges::to<std::vector> 物化视图来检查中间结果。
  • 兼容性:Views 可以与传统迭代器混合,但优先使用 Ranges 算法。
  • C++23 更新:添加了 std::views::stride(步进)、std::views::slide(滑动窗口)等新视图,扩展了功能。
  • 常见错误
    • 忽略生命周期,导致未定义行为。
    • 忘记导入 <ranges>
    • 对非 viewable range 使用视图(使用 std::views::all 包装)。
7. 总结

C++ views 是 Ranges 库的精华,提供了一种强大、惰性、可组合的方式来处理序列数据。它将函数式编程带入 C++,简化了复杂操作,同时保持了高性能。通过管道语法和丰富的内置视图,你可以编写更简洁、安全的代码。Views 特别适合数据处理管道、过滤转换和生成序列的场景。

http://www.dtcms.com/a/533993.html

相关文章:

  • Java基础复习-上
  • 长沙网站改版西安企业建站系统模板
  • 龙岩网站优化个人做网站好吗
  • 无锡网站托管国产一级a做爰片免费网站
  • 巨野住房和城乡建设局网站wordpress多站共享授权码
  • 哪些网站可以做免费广告推广有哪些建设工程类网站
  • 禹城有做网站常见网络营销工具
  • 如何做彩票网站的源码可以做游戏的网站有哪些
  • 龙元建设集团股份有限公司网站地址网站设计素材网站
  • 苏州基础网站建设音乐播放网站开发pc端
  • 多线程之屏障(Barrier)
  • Matplotlib 安装指南
  • 155.最小栈
  • 网站开发和移动开发成都市房产信息网
  • 乐平市建设局网站精品课程网站建设设计方案
  • 厦门网站建设方案服务公司网站开发建设什么会计科目
  • 深圳网站建设相关推荐wordpress启用主题
  • 为什么360极速浏览器X新建标签页总在所有标签页的最右侧打开?用键盘Ctrl+T新建标签页总在所有标签页最右侧打开解决办法。
  • 中国建设银行网站登陆安徽百度关键词优化
  • 德州网站开发培训网站策划专员招聘
  • 想找个人做网站广州微网站建设
  • 南通e站网站建设外国人在中国注册公司需要什么条件
  • 婚纱摄影网站开题报告河北黄页网
  • 网站 色调企业网站做多大尺寸
  • 人工智能学习中深度学习之python基础之高阶函数
  • 网站前台乱码2022最新永久地域网名
  • 备案网站可以做影视站网站开发中视屏怎样编辑到网页上
  • 株洲网站制作公司在哪里郑州网络推广广告公司
  • 做动图的网站知乎微网站站点名称
  • 提示词类型与示范学习机制(附 Qwen 模型实战代码)