You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

过滤函数开销大时,如何处理std::ranges过滤视图?

问题与解决方案:避免std::views::filter中昂贵谓词被重复调用

当执行类似 std::ranges::to<std::vector>(vals | std::views::filter(expensive)); 的代码时,expensive谓词会被调用两次:一次是std::vector构造函数通过std::distance(beg, end)计算元素数量以预分配内存,另一次是实际遍历范围构造元素时。

针对你提出的思路分析

1. 让过滤视图表现为std::ranges::input_range

可以实现。std::views::filter默认返回的是forward_range(若源范围为forward_range),如果让它降级为input_range,std::ranges::to就无法提前计算元素数量,只能通过back_inserter逐个插入,从而避免两次遍历。

实现方式是给过滤视图包裹一层仅满足input_range要求的适配器:比如自定义一个适配器,让其迭代器仅实现input_iterator的接口(不支持多遍遍历、仅允许与std::default_sentinel比较operator==),这样范围会被识别为input_range,触发std::ranges::to的单遍插入逻辑。

2. 直接使用std::ranges::copy + back_inserter

这是最直接有效的方案。无论是否预先为result预留空间,这种方式只会遍历范围一次,expensive仅被调用一次。如果尝试手动统计数量再预留空间,反而会再次调用expensive,得不偿失,因此直接用back_inserter逐个插入是这类场景的最优选择之一。

3. 其他可行方案

  • 提前缓存过滤结果:如果源数据不会变更,可以先将过滤后的元素存入中间容器(如std::vector),后续直接使用该容器。但该方案不适用于一次性流数据。
  • 自定义带缓存的过滤适配器:实现一个类似filter的适配器,在第一次遍历时同时统计元素数量并缓存所有符合条件的元素,之后一次性分配内存构造目标容器。这种方式需要自行管理缓存,适合频繁处理昂贵过滤逻辑的场景。

关于实现std::views::expensive_filter的合理性

完全合理。这个适配器的核心是让返回的范围仅满足std::ranges::input_range,从而禁用所有依赖forward_range的预计算逻辑(比如std::ranges::to的内存预分配),强制采用单遍遍历方式,从根源上避免昂贵谓词被重复调用。

实现时需注意:

  • 适配器的迭代器仅满足input_iterator要求,不支持多遍遍历
  • 过滤逻辑与std::views::filter保持一致,仅限制范围的迭代器类别

内容的提问来源于stack exchange,提问作者Ben

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 07:03:28