std::rotate是唯一安全且零额外拷贝的区间循环移位标准解法,基于三次反转实现o(n)时间o(1)空间,天然支持重叠、自动适配pod与可移动类型,但需严格满足first≤middle≤last且正确换算middle位置。

std::rotate 是唯一安全且零额外拷贝的标准解法
直接用 std::rotate,别手写循环、别拼 std::copy + std::move、更别裸调 memcpy —— 它是标准库中唯一专为「区间循环移位」设计的算法,天然支持重叠、不构造新对象、对 POD 和可移动类型自动选最优路径。
常见错误现象:用 std::copy(std::make_move_iterator(first), last, dest) 移动重叠区间,结果部分元素被提前覆盖或析构,行为未定义;或写 for 循环逐个 std::move(*src++) = *dst++,既破坏缓存局部性,又可能因类型无移动语义而退化为深拷贝。
-
std::rotate内部用三次反转实现,时间 O(n)、空间 O(1),不依赖元素是否可默认构造 - 对
int、float等 trivial 类型,编译器通常内联为memmove或 SIMD 指令 - 对
std::string、std::vector等可移动类型,会调用移动构造/赋值(前提是类真正实现了T(T&&)且未被=delete) - 参数必须满足
first ≤ middle ≤ last,否则未定义行为(不抛异常,可能静默错乱)
怎么写才不踩边界坑:middle 位置必须精确换算
std::rotate 不是 “左移 k 位” 接口,而是 “以 middle 为分界,把 [first, middle) 搬到 [middle, last) 后面”。写错 middle 就等于搬错段。
典型场景换算:
- 把前 k 个元素移到末尾(等效循环左移 k):
std::rotate(v.begin(), v.begin() + k, v.end()) - 把后 k 个元素移到开头(等效循环右移 k):
std::rotate(v.begin(), v.end() - k, v.end()) - 对原生数组:
std::rotate(arr, arr + offset, arr + size),注意arr + size必须是合法末尾地址 - 务必先做
k %= v.size(),否则v.end() - k可能越界;负数偏移要转正:k = ((k % n) + n) % n
自定义类型移动失效?检查三件事
表面调用了 std::rotate,但性能暴跌,大概率是移动语义没生效,退化成深拷贝。
- 类必须显式声明移动构造函数
T(T&&)且未被=delete;成员变量也得支持移动(比如裸指针需手动 move,std::vector默认支持) - 移动操作应标记
noexcept,否则某些标准库实现(如std::vector::resize)会回避移动而选拷贝 - 确认是否真走移动路径:在移动构造函数里加日志或断点,别信“看起来像移动了”
- 对 trivial 类型(如
struct {int x; float y;}),std::rotate会跳过构造/析构,直接内存块操作,最快
什么情况下该放弃 std::rotate,改用三步翻转法
仅当以下任一条件成立时,才考虑手写三步翻转(reverse ×3):
- 目标是裸内存(非容器),且编译器老旧(C++11 之前),
std::rotate实现不可靠 - 类型不可拷贝、不可默认构造(如某些嵌入式 sensor buffer),
std::rotate内部 swap 可能失败 - 需要严格保证 O(1) 额外空间(
std::rotate对非随机访问迭代器可能用临时存储) - 手写时注意:
reverse(first, last)循环条件必须是first ,不能用 <code>;空或单元素要提前返回;<code>last是尾后指针
真正难的不是怎么写,而是判断什么时候不该用 std::rotate —— 大多数人卡在“以为自己用了 move 就安全”,却没验证类型 trait 或检查迭代器有效性。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











