std::is_trivially_assignable_v 是编译期判断类型是否平凡可赋值的正确表达式,仅检查能否 memcpy 级别赋值,不执行实际操作,需配合 std::is_trivially_copyable_v 使用以确保安全 memcpy。

std::is_trivially_assignable 是编译期判断,不是运行时函数
它不执行任何赋值操作,只在模板实例化时检查类型是否满足「平凡可赋值」的底层规则——即编译器能否直接 memcpy 级别复制。这和 operator= 是否被用户定义、是否 noexcept、是否有虚函数/非平凡成员等强相关。
常见错误现象:std::is_trivially_assignable_v<t const t></t> 返回 false,但你手写了一个空的 operator= ——只要显式定义了,哪怕内容为空,就不再是平凡的。
- 必须确保类/结构体没有用户声明的拷贝/移动赋值运算符
- 所有非静态数据成员和基类都必须是 trivially assignable
- 不能有虚函数、虚基类、引用成员、const 成员(除非是 Static const)
- 检查时用左值引用接收右值(如
T&接收const T&),否则语义不对
怎么正确写 std::is_trivially_assignable 的判断表达式
最常用且语义准确的是:std::is_trivially_assignable_v<t const t></t>。注意不是 T& 接收 T,也不是 const T& 接收 T& ——前者忽略 const 正确性,后者是“读取”而非“赋值”场景。
使用场景:做 SFINAE 或 static_assert 检查结构体能否安全用于 memcpy 优化或 POD 布局假设。
立即学习“C++免费学习笔记(深入)”;
参数差异示例:
struct Trivial { int x; double y; }; struct NonTrivial { int x; NonTrivial& ref; }; // 引用成员 → 不平凡 static_assert(std::is_trivially_assignable_v<Trivial&, const Trivial&>, "OK"); static_assert(!std::is_trivially_assignable_v<NonTrivial&, const NonTrivial&>, "fails");
误判风险:继承、模板、对齐导致的隐式不平凡
即使结构体看起来全是基本类型,也可能因继承或模板推导意外破坏平凡性。比如从带虚函数的基类继承,或使用 std::Array(其内部实现可能含 non-trivial 赋值逻辑)。
性能影响很直接:如果误信 std::is_trivially_assignable 为 true 而手动 memcpy,而实际类型有自定义 operator=(哪怕没被调用),行为未定义。
- 多重继承中任一基类含虚表 → 整个派生类不平凡
-
std::vector<t></t>永远不 trivial(析构/赋值非平凡),但std::array<t n></t>取决于T - 编译器对
[[no_unique_address]]成员的处理可能影响平凡性判定(c++20 起) - 某些平台 ABI 对齐要求可能导致结构体内存布局与 memcpy 不兼容,即使类型判定为 trivial
替代方案:用 memcpy 前先确认 trivial copy + trivial assign
仅靠 std::is_trivially_assignable 不够。结构体要安全 memcpy,还需同时满足 std::is_trivially_copyable_v<t></t> ——它涵盖构造、析构、复制三方面平凡性。
容易踩的坑:有人用 std::is_pod_v<t></t>,但它在 C++20 已弃用,且要求更严(比如不允许 private 成员),实际约束过强。
- 推荐组合判断:
std::is_trivially_copyable_v<t> && std::is_trivially_assignable_v<t const t></t></t> - 若用于序列化或跨线程共享,还要确认无指针/句柄/状态依赖(
std::is_trivially_copyable不保证逻辑正确性) - Clang/GCC 在
-fsanitize=undefined下会捕获 memcpy 非 trivial 类型的行为,建议开启
事情说清了就结束