std::ranges::transform与views::transform转换哪个更优?二者有何差异?
关于std::ranges::transform与views::transform的选择及代码对比
两者的核心差异与适用场景
- std::ranges::transform:属于立即执行的算法,调用时就会遍历输入范围,立刻完成所有元素的转换并写入输出容器。适合需要直接生成目标容器,或者转换逻辑带有副作用的场景。
- std::ranges::views::transform:属于延迟执行的视图,创建视图时不会做任何实际转换操作,只有当你遍历这个视图(比如用它构造容器、循环迭代)时,才会逐个对元素执行转换。适合构建多步骤的视图管道(比如链式调用过滤、截取等操作),或者不需要立刻生成完整容器的场景。
两段代码的差异对比
先看两段代码:
第一段代码:
std::vector<source> sources; std::vector<dest> dests; dests.reserve(sources.size()); std::ranges::transform(sources.begin(), sources.end(), std::back_inserter(dests), trFunction);
第二段代码:
std::vector<source> sources; auto view = sources | std::ranges::views::transform(trFunction); std::vector<dest> dests{ view.begin(), view.end() };
两者的具体差异:
- 执行时机:
- 第一段调用
std::ranges::transform时,所有元素的转换操作就会立刻执行,结果直接插入到dests中。 - 第二段创建view时啥都没干,直到用view构造dests的那一刻,才会遍历视图并逐个完成转换。
- 第一段调用
- 内存分配:
- 第一段手动调用了
reserve,确保dests只做一次内存分配,避免多次扩容。 - 第二段用迭代器范围构造vector时,因为
views::transform是有大小的范围(当输入容器是sized时),vector会自动计算所需内存并一次性分配,效果和手动reserve完全一样,不需要额外调用reserve。
- 第一段手动调用了
- 灵活性:
- 第二段的view可以被复用,或者和其他视图组合(比如
sources | views::transform(trFunction) | views::filter(...)),后续扩展处理逻辑更方便;而第一段是直接生成目标容器,没法再基于原转换逻辑做后续操作。
- 第二段的view可以被复用,或者和其他视图组合(比如
- 副作用注意事项:
如果trFunction带有副作用(比如修改外部变量),两段代码的执行次数是一样的——都是每个元素执行一次。但如果view被多次遍历(比如后续再用这个view构造另一个容器),trFunction会被重复执行,这一点要特别留意。
优先选用哪种写法?
- 如果只是单纯把一个容器转换为另一个容器,两种写法的效果几乎没差别,但第二段更简洁,符合现代C++的range编程风格,代码可读性更高。
- 如果需要构建复杂的处理流程(比如转换后还要过滤、截取一部分元素),优先用
views::transform,延迟执行可以避免创建不必要的中间容器,效率更高。 - 如果转换逻辑有副作用,且你希望立刻完成所有转换,或者需要精细控制输出容器的插入过程(比如不是直接插入末尾),那选
std::ranges::transform更合适。
内容的提问来源于stack exchange,提问作者Morse
相关产品推荐
相关产品推荐

