类型是平凡可移动的当且仅当同时满足std::is_trivially_move_constructible_v和std::is_trivially_move_assignable_v为true,二者缺一不可;仅std::is_trivial_v为true不充分,因其要求更严格。

如何用 std::is_trivially_move_constructible 和 std::is_trivially_move_assignable 判断平凡可移动
类型是否“平凡可移动”,C++ 标准没有直接提供 std::is_trivially_movable 这样的单一 trait。必须同时满足两个条件:平凡移动构造 + 平凡移动赋值。缺一不可。
注意:std::is_trivially_move_constructible<t></t> 检查的是 T(T&&) 是否为 trivial(即编译器生成的默认移动构造函数,且所有子对象也满足),不是“是否存在该函数”。同理,std::is_trivially_move_assignable<t></t> 检查 T& operator=(T&&) 是否 trivial。
- 两者都为
true→ 类型是平凡可移动的(可被memmove级别优化) - 任一为
false→ 不是平凡可移动;哪怕你写了= default,只要成员里有非平凡移动语义的子对象(如std::string、std::vector),结果就是false - 对
const、引用、函数类型等,这两个 trait 通常为false(不满足移动语义前提)
为什么不能只看 std::is_trivial<t></t>
std::is_trivial<t></t> 是更强的条件:要求类型同时满足平凡默认构造、平凡拷贝、平凡移动、平凡析构,且无虚函数/虚基类等。但平凡可移动只要求移动相关操作是 trivial 的——它允许类型有非平凡的拷贝或默认构造。
典型反例:struct S { S() { /* 非平凡构造 */ } int x; }; —— std::is_trivial_v<s></s> 是 false,但 std::is_trivially_move_constructible_v<s></s> 和 std::is_trivially_move_assignable_v<s></s> 都是 true(因为移动操作由编译器隐式生成且无副作用)。
立即学习“C++免费学习笔记(深入)”;
- 误用
std::is_trivial会过度保守,把实际可平凡移动的类型判为否 - 尤其在实现泛型容器(如自定义 vector)做 memcpy 优化路径时,错判会导致安全漏洞或性能退化
- 标准库中
std::vector的移动实现在满足平凡可移动时就走memmove分支,依据正是这两个 trait
常见踩坑:自定义移动函数破坏平凡性
只要显式声明了移动构造函数或移动赋值运算符(哪怕写成 = default),该函数就不再是“implicit”——但关键在于:= default 在类定义内且满足条件时,仍可产生 trivial 函数;而一旦出现在类外定义,或类中有 const 成员 / 引用成员 / 不可移动成员,则 trivial 性立即丢失。
- 错误写法:
struct Bad { const int x; Bad(Bad&&) = default; };</code> → <code>std::is_trivially_move_constructible_v<Bad></code> 是 <code>false</code>(<code>const</code> 成员禁止移动构造)</li><li>危险写法:<pre class="brush:php;toolbar:false;"><code>struct Oops { std::string s; Oops(Oops&& o) : s(std::move(o.s)) {} };</code> → 显式定义导致非 trivial,即使逻辑上只是转发</li><li>安全写法:<pre class="brush:php;toolbar:false;"><code>struct Good { std::string s; Good(Good&&) = default; };</code> → 若 <code>std::string</code> 本身非平凡移动,则 <code>Good</code> 也不平凡;这是正确行为,不是 bug</li></ul><H3>运行时不可检测,必须编译期断言</H3><p>平凡可移动是编译期属性,无法靠 <code>if</code> 或 <code>typeid</code> 判断。必须用 <code>static_assert</code> 或 <code>constexpr if</code> 做分支。</p><p>例如在模板中启用 memcpy 优化:</p><pre class="brush:php;toolbar:false;"><pre class="brush:php;toolbar:false;">template<typename T> void move_range(T* dst, T* src, size_t n) { if constexpr (std::is_trivially_move_constructible_v<T> && std::is_trivially_move_assignable_v<T>) { std::memmove(dst, src, n * sizeof(T)); } else { for (size_t i = 0; i < n; ++i) { new (&dst[i]) T(std::move(src[i])); } } }这里漏掉任一 trait,或者用了
std::is_pod(已弃用)或 <code>std::is_standard_layout(无关),都会导致行为错误。最常被忽略的是:平凡可移动 ≠ 可 POD,也 ≠ 可 memcpy(比如有虚表的类可能 trivially move-assignable,但绝不能memmove)。


















