C++reverse_copy反向复制序列
reverse_copy:不是“反转数组”,而是把倒序结果“搬”到别处
你有没有试过,想把一个 vector 里的元素倒过来存进另一个容器,但又不想动原数据?std::reverse 确实能反转,但它会原地改——就像把一排书从左到右调个个儿,书架上原来的顺序就没了。而 reverse_copy 不碰原序列一根手指头,只安静地把倒着读出来的结果,一五一十抄到你指定的地方。它不制造混乱,只负责搬运。
reverse_copy 是 <algorithm> 里的一个“轻量级搬运工”,签名长这样:
template<class BidirIt1, class BidirIt2>
BidirIt2 reverse_copy(BidirIt1 first, BidirIt1 last, BidirIt2 d_first);
注意三个参数:源区间的起止迭代器(first, last),以及目标起始位置(d_first)。它从 last-1 开始往前遍历,把每个元素依次赋值给 d_first、d_first+1、d_first+2……直到源区间走完。返回值是目标区间的“下一个空位”——这点很实用,尤其当你连续拼接多个反向片段时,它自然衔接到下一段起点。
举个实在的例子。假设你有一串传感器采样值,按时间正序存着:
std::vector<int> samples = {10, 25, 42, 33, 18};
std::vector<int> reversed(samples.size()); // 必须提前分配足够空间!
这时候写:
std::reverse_copy(samples.begin(), samples.end(), reversed.begin());
// reversed 现在是 {18, 33, 42, 25, 10}
没改 samples,也没越界——因为 reversed 大小刚好够。这是新手最容易踩的坑:目标空间不足,行为未定义。 它不会帮你 push_back,也不会扩容,它只管“抄”,抄到哪算哪。如果你用 std::back_inserter,得换 std::copy 配合 std::make_reverse_iterator,那是另一条路。
更常见的需求,是把反向结果塞进一个已有容器的末尾,或者和别的数据拼起来。比如日志系统里,新来的几条记录要“插到历史反向视图的开头”:
std::vector<std::string> history = {"login", "open_file", "save"};
std::vector<std::string> recent = {"exit", "close_tab"};
// 想让 recent + 反向的 history 构成完整回溯链
std::vector<std::string> trace;
trace.reserve(recent.size() + history.size());
// 先搬 recent(正序)
std::copy(recent.begin(), recent.end(), std::back_inserter(trace));
// 再搬 history 的反向副本
std::reverse_copy(history.begin(), history.end(), std::back_inserter(trace));
// trace = {"exit", "close_tab", "save", "open_file", "login"}
这里 std::back_inserter 和 reverse_copy 能配合,是因为 back_inserter 返回的是一个输出迭代器,支持 *it = value 和 ++it——reverse_copy 正好只依赖这两项操作。它不要求目标是随机访问,甚至可以是 std::ostream_iterator,直接打印反向内容:
std::vector<char> word = {'h', 'e', 'l', 'l', 'o'};
std::reverse_copy(word.begin(), word.end(),
std::ostream_iterator<char>(std::cout, ""));
// 输出 "olleh"
这比先 reverse 再 for 打印少两行,也更清晰表达了意图:我要的是“反向呈现”,不是“反向修改”。
还有一个容易被忽略的细节:reverse_copy 对输入只要求双向迭代器(BidirectionalIterator),这意味着它能处理 list、deque,甚至你自己写的满足条件的类。但别指望它对 forward_list 或 istream_iterator 奏效——它们不支持 --it,而反向遍历必须能往回走。换句话说,它天然排斥单向流式数据,这是设计上的诚实,不是缺陷。
再聊点实际调试经验。当你发现 reverse_copy 后目标容器全是默认值(比如 0 或空字符串),第一反应不该是怀疑算法,而是立刻检查:目标迭代器是否指向有效内存?是否越界? 我曾在一个嵌入式项目里,把 d_first 错写成 buffer + size(本该是 buffer),结果所有拷贝都落在 buffer 外,静默失败。加一行 assert(d_first != nullptr) 或用 std::span 封装目标区域,能省掉大半排查时间。
最后说句实在话:reverse_copy 不是什么高频炫技函数。多数时候,我们确实只需要 std::reverse 或者直接倒序遍历。但它存在的意义,是在那些“必须保留原始顺序+同时提供反向视图”的场景里,提供一种零副作用、可预测、符合 RAII 直觉的解法。比如构建不可变数据结构的视图层,或者实现某个协议要求的字节序翻转而不污染原始缓冲区。
它不声张,不抢戏,但当你需要它时,它就在那里,稳稳地把倒过来的世界,原样交到你指定的位置。


还没有评论,来说两句吧...