std::shift_left和std::shift_right是C++20引入的容器元素平移算法,非位运算,不改变容器大小,仅移动已有元素并用默认值填充空缺;其行为依赖合法偏移量,越界时不报错但会静默填充默认值。

std::shift_left 和 std::shift_right 是 C++20 引入的真正“平移容器元素”的算法,不是位运算,也不修改容器大小,只移动已有元素位置——用错地方会静默失效或越界。
std::shift_left(v.begin(), v.end(), n) 的调用必须满足三个前提
这个函数不会帮你检查 n 是否合法,也不会自动处理空范围或越界偏移:
-
first和last必须构成有效范围(first ),否则行为未定义 -
n必须是非负整数;传负值不报错但结果不可预测(标准未定义) - 实际可移动长度为
std::distance(first, last),若n >= 距离,则整个范围被设为默认构造值(对 int 是 0,对 string 是空串)
常见错误现象:std::shift_left(v.begin(), v.end(), 5) 在 v.size() == 3 时不会报错,但 v 全变 0 —— 这不是 bug,是标准规定的行为。
std::shift_left 与 std::rotate 的关键区别在哪
两者都改变元素顺序,但语义完全不同:
立即学习“C++免费学习笔记(深入)”;
-
std::rotate是循环移位:把 [first, middle) 搬到 [middle, last) 后面,不丢数据 -
std::shift_left是“左推”:前n个元素被覆盖,末尾n个位置填默认值,总长度不变 - 性能上,
std::shift_left是 O(n) 移动 + O(n) 默认构造;std::rotate是 O(n) 移动,无构造开销
使用场景举例:你有一段日志缓冲区 std::vector<LogEntry> buf(1024),每次新 entry 插入头部,旧 entry 自动挤出尾部 —— 这时该用 std::shift_right(buf.begin(), buf.end(), 1),再赋值给 buf[0],而不是 rotate。
ranges::shift_left 在 C++23 中怎么写更安全
C++23 提供了范围适配版本,能避免迭代器配对错误,但要注意它返回的是一个 std::ranges::subrange,不直接修改原容器:
std::vector<int> v = {1,2,3,4,5};
auto r = std::ranges::shift_left(v, 2); // r 是 subrange,v 本身未变!
// 正确做法是:
std::ranges::shift_left(v, 2); // 无返回值重载才真改 v
容易踩的坑:
- 忘记 C++23 有两个重载:有返回值的(只读视图)、无返回值的(就地修改),选错就白调用
-
std::ranges::shift_left要求 range 可写且元素可移动/默认构造,对std::vector<const int>或自定义不可默认构造类型编译失败 - 它不支持输入迭代器(如
std::istream_iterator),仅适用于 random_access 或至少 forward_range
别和位运算 > 混淆,连头文件都不同
这是最常被误搜的点:std::shift_left 在 <algorithm>,而位运算符是语言内置,不需要 include。有人在调试时看到 operator<< 报错,却去查 std::shift_left 文档,纯属方向错误。
判断依据很简单:如果代码里出现 a << b 或 std::cout << x,那跟 std::shift_left 完全无关;只有当你明确写了 std::shift_left(it1, it2, n),才进入这个算法域。
复杂点在于:同名不同义的函数在工程中可能共存(比如信号处理库自己实现了 shift_left 循环移位),这时务必确认命名空间和头文件,::shift_left 和 std::shift_left 不是一回事。

















