能否借助views::move实现C++原地算法?相关技术细节问询
关于std::vector原地保留重复元素的Ranges管道实现问题
问题1:能否借助views::move用给定的管道实现原地处理?
不行。你写的这段管道只是生成了一个视图,本质是对原vector元素的“观察层”,并不会直接修改原容器的内容或大小。views::move只是让视图的元素以右值引用的形式被访问,但整个管道操作不会主动把符合条件的元素“整理”到原容器的前端,也不会删除不符合条件的元素。单独运行这段代码,原vector不会有任何变化,达不到原地处理的目的。
问题2:针对原方案的延伸问题
既然原方案本身无法完成原地修改,这里针对你关心的点补充说明:
- 如何确定结果末尾并删除剩余元素:如果要基于视图完成原地修改,需要先把视图中的元素移动(或拷贝)到原vector的起始位置,然后用
vector::erase截断从有效元素末尾到原容器末尾的部分。比如用std::ranges::move把视图内容移到v.begin(),得到的返回值就是新的有效元素末尾迭代器,再用v.erase(返回值, v.end())即可。 - 额外内存分配或拷贝:如果用
std::ranges::move(不是views::move)把视图元素移到原容器前端,整个过程不会分配额外内存,因为是原地覆盖原容器的元素,没有临时容器产生,只是元素的所有权转移(对于可移动类型)或拷贝(对于不可移动类型)。 - VS2022对
views::move的支持:VS2022从17.0版本开始支持C++20标准中的std::views::move,后续的17.x版本逐步完善了相关Ranges特性的兼容性,目前在最新版本中可以稳定使用。
问题3:如何用std::ranges管道实现原地处理?
正确的做法是先通过管道生成符合条件的元素视图,再将视图内容原地覆盖到原vector的前端,最后截断容器。示例代码如下:
#include <vector> #include <ranges> #include <algorithm> int main() { std::vector<int> v = {1,1,1,1,2,2,3,3,3,3,3,4}; const auto repeat_size = 4; // 生成符合条件的元素视图:按连续相等分组,过滤出长度≥4的组,再合并 auto filtered_view = v | std::views::chunk_by(std::ranges::equal_to{}) | std::views::filter([repeat_size](auto r) { return r.size() >= repeat_size; }) | std::views::join; // 将视图中的元素移动到原vector的起始位置,得到新的有效末尾迭代器 auto new_end = std::ranges::move(filtered_view, v.begin()); // 删除末尾多余的元素,完成原地修改 v.erase(new_end, v.end()); // 此时v的内容是{1,1,1,1,3,3,3,3,3} return 0; }
这段代码的核心逻辑:
- 管道生成的视图只负责筛选需要保留的元素,不占用额外内存;
std::ranges::move把视图元素原地移到容器前端,避免不必要的拷贝(元素支持移动时);- 最后通过
erase截断容器,真正完成原地修改,全程无额外内存分配。
内容的提问来源于stack exchange,提问作者Damir Tenishev
相关产品推荐
相关产品推荐

