C++中已被移动的对象能否再次被移动?
关于C++中被移动后对象的复制/再次移动安全性问题
核心结论
被移动后的对象处于有效但未指定的状态,C++标准仅保证能对其执行赋值操作和销毁操作,其他操作(复制、再次移动)的安全性完全取决于对象类型的具体实现,不能一概而论。
一、被移动后的对象能否复制或再次移动?
- 标准无强制安全承诺:“未指定状态”意味着对象内部值不确定,除非该类型的官方文档明确约定了移动后的具体状态(比如部分标准库类型会承诺移动后对象为空),否则无法预知复制/再次移动的结果。
- 具体场景示例:
- 对于
std::string、std::vector这类标准库类型,主流实现会将被移动后的对象置为空状态,此时复制会得到空对象,再次移动也不会触发问题——但这是实现方的约定,而非C++标准的强制要求。 - 对于自定义类型,如果移动构造函数未将源对象置于确定安全状态(比如仅转移资源但未把源对象的指针置空),再次移动可能导致重复释放资源,复制则可能得到包含无效资源的对象,触发未定义行为。
- 对于
二、容器重分配时再次移动已被移动的对象是否安全?
以vector重分配为例:
vector重分配时优先调用元素的移动构造函数(若该函数标记为noexcept),否则会退用复制构造函数。- 若移动构造函数是
noexcept且实现正确:只要移动构造能处理任何有效状态的源对象(哪怕是未指定状态),再次移动就是安全的——因为移动构造的要求本身是“移动后源对象仍处于有效但未指定状态”,所以即使源对象已被移动过,只要它仍有效,移动构造就能正常执行(结果仍未指定,但不会触发未定义行为)。 - 若使用复制构造函数:情况和前文一致,复制结果未指定,若复制操作依赖源对象的特定状态,可能触发未定义行为。
实操建议
- 永远不要依赖被移动后对象的具体状态,除非类型文档明确说明。
- 自定义类型实现移动语义时,建议将被移动后的源对象置于确定的安全状态(比如把指针置空、容器清空),确保后续复制或再次移动不会出问题。
- 对于标准库类型,可放心依赖主流实现的约定(比如移动后为空),但需明确这不是标准强制要求,极端场景下可能有例外。
内容的提问来源于stack exchange,提问作者ABu
相关产品推荐
相关产品推荐

