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

std::ranges::ref_view,std::ranges::owning_view, std::ranges::views::all

std::ranges::ref_view 

C++20 引入的一个范围适配器,用于将一个范围包装成一个轻量级的视图,该视图持有对原始范围的引用。它允许你在不复制原始范围的情况下对其进行操作,适用于需要传递范围但不希望复制数据的场景。

基本概念

  • 引用语义ref_view 持有对原始范围的引用,因此它不会复制或拥有原始范围的数据。

  • 轻量级ref_view 本身只是一个包装器,开销非常小。

  • 范围适配器ref_view 是一个范围适配器,可以将任何满足 range 概念的类型转换为一个视图。

使用场景

  • 当你需要传递一个范围但不希望复制数据时。

  • 当你需要将非视图范围(如容器)转换为视图时。

示例 1 

#include <iostream>
#include <vector>
#include <ranges>

int main() {
    // 创建一个 vector
    std::vector<int> vec = {1, 2, 3, 4, 5};

    // 使用 ref_view 包装 vector
    std::ranges::ref_view ref_vec = vec;

    // 使用 ref_view 进行范围操作
    for (int i : ref_vec | std::views::filter([](int x) { return x % 2 == 0; })) {
        std::cout << i << " ";  // 输出偶数
    }

    return 0;
}

解释

  1. 创建 ref_view

    • std::ranges::ref_view ref_vec = vec; 将 vec 包装成一个 ref_view

    • ref_vec 现在持有对 vec 的引用,而不是复制 vec 的内容。

  2. 范围操作

    • ref_vec | std::views::filter(...) 对 ref_vec 进行过滤操作,只保留偶数。

    • 由于 ref_view 是一个视图,操作是惰性的,只有在遍历时才会实际执行。

  3. 输出结果

    • 程序输出 2 4,即 vec 中的偶数。

示例 2

#include <iostream>
#include <ranges>
 
int main()
{
    const std::string s{"cosmos"};
 
    const std::ranges::take_view tv{s, 3};
    const std::ranges::ref_view rv{tv};
 
    std::cout
        << std::boolalpha
        << "call empty() : " << rv.empty() << '\n'
        << "call size()  : " << rv.size() << '\n'
        << "call begin() : " << *rv.begin() << '\n'
        << "call end()   : " << *(rv.end() - 1) << '\n'
        << "call data()  : " << rv.data() << '\n'
        << "call base()  : " << rv.base().size() << '\n' // ~> tv.size()
        << "range-for    : ";
 
    for (const auto c : rv)
        std::cout << c;
    std::cout << '\n';
}

Output:

call empty() : false
call size()  : 3
call begin() : c
call end()   : s
call data()  : cosmos
call base()  : 3
range-for    : cos

注意事项

  • 生命周期ref_view 只是持有对原始范围的引用,因此必须确保原始范围在 ref_view 使用时仍然有效

  • 不可复制ref_view 本身是不可复制的,因为它持有对原始范围的引用。

总结

std::ranges::ref_view 是一个非常有用的工具,特别是在需要传递范围但不希望复制数据的情况下。它通过引用语义提供了一种轻量级的方式来操作范围,同时保持了代码的简洁性和效率。

 

std::ranges::owning_view 

C++20 引入的一个范围适配器,用于持有并管理一个范围的所有权。与 ref_view 不同,owning_view 拥有底层范围对象适用于需要延长临时范围生命周期的场景,避免悬垂引用。以下是详细说明及示例:


核心特性

  1. 所有权语义
    owning_view 通过移动或复制构造,完全拥有底层范围对象当 owning_view 被销毁时,其管理的范围对象也会被销毁。

  2. 生命周期管理
    适用于包装临时范围(如函数返回的右值),确保其在视图生命周期内有效。

  3. 轻量级包装
    本身不复制数据,仅通过移动或复制管理原始范围的所有权。


使用场景

  • 需要持有临时生成的范围(如函数返回的容器)。

  • 避免悬垂引用,确保底层数据的生命周期与视图一致。


示例 1

#include <iostream>
#include <vector>
#include <ranges>

// 返回临时 vector 的函数
auto get_data() {
    return std::vector{1, 2, 3, 4, 5};
}

int main() {
    // 包装临时范围:转移所有权到 owning_view
    auto ov = std::ranges::owning_view(get_data());

    // 操作视图:过滤偶数
    auto even = ov | std::views::filter([](int x) { return x % 2 == 0; });

    for (int x : even) {
        std::cout << x << " ";  // 输出 2 4
    }

    return 0;
}

关键点解析

  1. 构造 owning_view

    • 通过右值构造(如临时对象或 std::move 后的左值):

      std::vector<int> vec = {1, 2, 3};
      auto ov = std::ranges::owning_view(std::move(vec));  // vec 被转移,不再可用
  2. 生命周期安全
    owning_view 持有原始范围的所有权,即使原始范围是临时对象,其生命周期也会延长至视图销毁。

  3. 与 ref_view 对比

    • ref_view 仅持有引用,不管理生命周期:

      // 危险!临时对象会立即销毁,导致悬垂引用
      auto rv = std::ranges::ref_view(get_data());
    • owning_view 安全持有临时对象的所有权。

示例 2

#include <cassert>
#include <iostream>
#include <ranges>
#include <string>
 
int main()
{
    using namespace std::literals;
    std::ranges::owning_view ov{"cosmos"s}; // the deduced type of R is std::string;
                                            // `ov` is the only owner of this string
    assert(
        ov.empty() == false &&
        ov.size() == 6 &&
        ov.size() == ov.base().size() &&
        ov.front() == 'c' &&
        ov.front() == *ov.begin() &&
        ov.back() == 's' &&
        ov.back() == *(ov.end() - 1) &&
        ov.data() == ov.base()
    );
 
    std::cout << "sizeof(ov): " << sizeof ov << '\n' // typically equal to sizeof(R)
              << "range-for: ";
    for (const char ch : ov)
        std::cout << ch;
    std::cout << '\n';
 
    std::ranges::owning_view<std::string> ov2;
    assert(ov2.empty());
//  ov2 = ov; // compile-time error: copy assignment operator is deleted
    ov2 = std::move(ov); // OK
    assert(ov2.size() == 6);
}

Possible output:

sizeof(ov): 32
range-for: cosmos

注意事项

  • 仅接受右值
    构造时必须传入右值(如临时对象或显式移动的左值),否则编译失败。

  • 不可复制
    由于所有权唯一,owning_view 不可复制,但可以移动:

    cpp

    复制

    auto ov2 = std::move(ov);  // 合法,所有权转移

与 views::all 的关系

views::all 根据输入范围类型自动选择 ref_view(左值)或 owning_view(右值):

cpp

复制

auto v1 = std::views::all(vec);       // ref_view(左值)
auto v2 = std::views::all(get_data()); // owning_view(右值)

总结

std::ranges::owning_view 提供了一种安全持有范围所有权的方式,尤其适用于管理临时对象的生命周期。通过所有权语义,避免了悬垂引用问题,是处理右值范围时的理想选择。

std::ranges::views::all 

C++20 Ranges 库中的一个工具,用于将任何范围(如容器、数组等)适配为一个视图,确保其可以与其他范围适配器组合使用。它根据输入范围的值类别(左值或右值)选择适当的视图类型,以安全地管理生命周期。

作用详解

  1. 适配为视图:将输入范围转换为视图,使其支持惰性求值和管道操作。

  2. 生命周期管理

    • 对左值范围,生成 ref_view,持有原范围的引用(需确保原范围的生命周期长于视图)。

    • 对右值范围,生成 owning_view拥有范围的所有权(避免悬垂引用)。

  3. 隐式转换在管道操作中,非视图范围会自动调用 views::all,但显式使用可增强代码清晰度。

示例代码

示例 1:左值范围生成引用视图
#include <vector>
#include <ranges>
#include <iostream>

int main() {
    std::vector<int> vec{1, 2, 3, 4, 5};
    auto view = std::views::all(vec); // ref_view,引用vec

    for (int i : view) {
        std::cout << i << ' '; // 输出:1 2 3 4 5
    }
    // vec 必须在此处存活
}

 示例 2:右值范围生成拥有视图

#include <vector>
#include <ranges>
#include <iostream>

int main() {
    auto view = std::views::all(std::vector{1, 2, 3}); // owning_view,拥有临时对象

    for (int i : view) {
        std::cout << i << ' '; // 输出:1 2 3
    }
}
示例 3:与其他适配器组合
#include <vector>
#include <ranges>
#include <iostream>

int main() {
    std::vector<int> vec{1, 2, 3, 4, 5};
    auto even = [](int i) { return i % 2 == 0; };
    auto square = [](int i) { return i * i; };

    // 组合操作:过滤偶数 -> 平方
    auto result = vec | std::views::filter(even) 
                     | std::views::transform(square);

    for (int i : result) {
        std::cout << i << ' '; // 输出:4 16
    }
}
示例 4:显式转换以存储视图

相关文章:

  • 【TypeScript】ts在vue中的使用
  • 电脑显示屏亮度怎么调?电脑屏幕亮度调节步骤介绍
  • laravel11设置中文语言包
  • 技术架构和工程架构区别
  • 【论文解读】《C-Pack: Packed Resources For General Chinese Embeddings》
  • 深入探讨K8s资源管理和性能优化
  • 深入解析 Spring 中的 BeanDefinition 和 BeanDefinitionRegistry
  • 链表理论基础
  • Java并发编程面试题:并发工具类(10题)
  • [2/11]C#性能优化-不要使用空析构函数-每个细节都有示例代码
  • Windows和AD域提权枚举脚本及工具介绍
  • python-leetcode-乘积最大子数组
  • 【CI/CD】Jenkins + Docker +SpringCloud微服务项目持续集成
  • 项目总结nk
  • Android ObjectBox数据库使用与集成指南
  • Doris系列之基础使用
  • Magma:多模态 AI 智体的基础模型
  • DeepSeek 助力 Vue3 开发:打造丝滑的下拉选择框(Dropdown Select)
  • 数据结构——基于单链表创建通讯录
  • LangChain教程 - RAG - PDF问答
  • “80后”北京市东城区副区长王智勇获公示拟任区委常委
  • 多个侵华日军细菌战部队留守名簿文件首次公布
  • 经济日报:美国滥施汽车关税损人不利己
  • 马上评丨岂能为流量拿自己的生命开玩笑
  • 比特币挖矿公司GRYP股价涨超171%:将与特朗普儿子创设的公司合并
  • 英国首相斯塔默住所起火,警方紧急调查情况