std::is_move_constructible仅表示类型可被右值构造,不保证无开销;真正反映“无开销移动”的是std::is_trivially_move_constructible,要求移动构造函数为编译器自动生成的trivial函数。

std::is_move_constructible 只检查可移动性,不保证无开销
std::is_move_constructible<T>::value 为 true 表示 T 可以用右值构造(即存在可行的移动构造函数),但它**完全不反映移动操作是否真正“无开销”**。比如 std::vector<int> 满足该 trait,但若其内部缓冲区为空,移动构造仍会执行指针交换——这确实是无开销的;而 std::array<int, 1000> 也满足该 trait,但它的移动构造实际是逐元素复制,和拷贝一样开销大。
所以这个 trait 的用途是“能否写 T t2{std::move(t1)};”,不是“这么写会不会快”。
真正判断“无开销移动”的关键是看是否为 trivially movable
标准库中没有直接叫 is_trivially_move_constructible 的 trait,但 C++11 起提供了 std::is_trivially_move_constructible(注意拼写,不是 is_move_constructible)。它要求:移动构造函数是 trivial(即编译器自动生成、不调用用户代码、仅做位拷贝)且未被显式删除或禁止。
-
std::is_trivially_move_constructible<T>::value为true是“无开销移动”的强信号(前提是类型不包含虚函数、非 trivial 成员等干扰项) - 它比
std::is_move_constructible更严格:例如std::string通常满足前者,但不一定满足后者(取决于实现是否用trivial移动构造) - 需配合
std::is_trivially_destructible<T>和std::is_trivially_move_assignable<T>才能确认整个移动语义链无开销
常见类型的实际表现要查实现或用 static_assert 验证
不要凭名字或直觉假设。比如:
立即学习“C++免费学习笔记(深入)”;
-
std::unique_ptr<int>:通常std::is_trivially_move_constructible为false(因为其移动构造函数是noexcept但非 trivial —— 它要置空源指针) -
std::optional<int>:若int是 trivial 类型,则它往往也是 trivially movable - 自定义 struct:只有当所有成员都 trivial 且未声明移动构造函数时,才可能 trivially movable
最稳妥的做法是在关键路径上加编译期断言:
static_assert(std::is_trivially_move_constructible_v<MyType>, "MyType must be trivially movable for zero-cost transfer");
注意 std::move 不改变类型属性,只转换为右值引用
std::move(x) 不会让 x “变成可无开销移动的类型”。它只是让表达式类型变为 T&&,从而触发重载决议。最终调用哪个构造函数,取决于 T 是否声明了移动构造函数、是否 trivial、是否 noexcept 等——这些全由类型定义决定,和 std::move 无关。
一个常被忽略的点:即使 T 是 trivially movable,如果移动构造函数被标记为 noexcept(false),某些标准容器(如 std::vector::resize)在扩容时仍可能退回到拷贝,因为它们依赖 is_nothrow_move_constructible 做优化决策。


















