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

为何std::view在转换为std::vector时会被求值两次?

为什么std::ranges::to会导致元素被求值两次?

核心原因是C++视图的惰性求值特性,加上std::ranges::to的部分实现策略:

  1. 视图是惰性的:filter和transform这类视图不会立即执行计算,只有当你遍历它们的时候,才会逐个处理元素。
  2. std::ranges::to的实现逻辑:对于无法直接获取大小的视图(比如filter后的视图,因为过滤后的元素数量不确定),你使用的Xcode 16.2中的libc++会先遍历一次视图,统计需要保留的元素数量,给vector预分配足够的内存,接着第二次遍历视图填充元素。而你的filter谓词带有打印这类副作用,两次遍历就导致了重复输出。

for循环遍历视图时,不需要提前知道元素数量,直接逐个处理,所以只会触发一次遍历,副作用只执行一次。

更优的写法

1. 避免视图操作带有副作用(最佳实践)

视图的谓词和转换函数应该是纯函数(输入相同则输出相同,无外部副作用),打印这类操作不应放在视图逻辑里。把副作用移到遍历阶段:

#include <vector>
#include <print>
#include <ranges>

int main() {
    const std::vector<int> input = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    auto output = input
    | std::views::filter([](const int n) { return n % 3 == 0; })
    | std::views::transform([](const int n) {return n * n; });

    const std::vector<int> vector = std::ranges::to<std::vector>( output );

    // 把打印移到这里
    for (int n : input) std::print("{} ", n);
    std::println("\ninput size: {} output size: {}", input.size(), vector.size() );
}

2. 用std::ranges::copy替代ranges::to避免两次遍历

如果必须保留视图中的副作用(不推荐,但应急可用),可以用std::ranges::copy配合std::back_inserter,这样只会遍历一次视图:

#include <vector>
#include <print>
#include <ranges>
#include <iterator>

int main() {
    const std::vector<int> input = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    auto output = input
    | std::views::filter([](const int n) { std::print("{} ", n); return n % 3 == 0; })
    | std::views::transform([](const int n) {return n * n; });

    std::vector<int> vector;
    // 提前预留空间减少扩容次数,用input大小作为上限
    vector.reserve(input.size());
    std::ranges::copy(output, std::back_inserter(vector));

    std::println("\ninput size: {} output size: {}", input.size(), vector.size() );
}

3. 强制单次遍历的ranges::to写法(C++23)

部分标准库实现支持通过std::ranges::to的重载,直接边遍历边插入容器,无需预先计算大小:

const std::vector<int> vector = std::ranges::to<std::vector>(output, std::unreachable_sentinel);

这个写法效果和copy+back_inserter一致,避免两次遍历。

总结

C++ ranges本身是高效的,问题出在视图副作用和ranges::to的预分配策略冲突。只要遵循视图的纯函数原则,或者选择合适的容器转换方式,就能避免这类问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 02:07:34