直接用std::is_trivially_copyable_v<T>(C++17起)或std::is_trivially_copyable<T>::value(C++11/14),它在编译期返回true当且仅当类型无虚函数、无虚基类、所有非静态成员及基类均平凡可复制,且特殊成员函数均为隐式默认;含std::string、std::vector或显式声明拷贝构造函数的类型均不满足。

怎么用 std::is_trivially_copyable 判断类型?
直接用 std::is_trivially_copyable_v<t></t>,C++17 起推荐用这个变量模板;C++11/14 则用 std::is_trivially_copyable<t>::value</t>。
它在编译期返回 true 当且仅当类型满足:有平凡拷贝构造、平凡移动构造、平凡拷贝赋值、平凡移动赋值、平凡析构,且不能有虚函数、虚基类,非静态成员也必须都是平凡可复制的。
常见误判点:含 std::string、std::vector、用户自定义拷贝构造函数(哪怕只声明不定义)的类,都会让 std::is_trivially_copyable_v 为 false。
示例:
立即学习“C++免费学习笔记(深入)”;
struct Plain { int x; double y; };
struct NotPlain { std::string s; };
static_assert(std::is_trivially_copyable_v<Plain>); // ✅
static_assert(!std::is_trivially_copyable_v<NotPlain>); // ✅
为什么 memcpy 前必须确认是平凡可复制?
因为只有平凡可复制类型才能安全地用 memcpy 或 std::bit_cast(C++20)做位级拷贝。否则行为未定义——比如拷贝含虚表指针的对象,目标对象的虚表可能失效;拷贝含内部引用计数的对象(如 std::shared_ptr),会破坏引用关系。
典型场景:
- 序列化到字节缓冲区(如网络发送、文件写入)
- 零拷贝共享内存结构体
- GPU 内存映射(需确保 host struct 可直接 memcpy 到 device)
错误做法:memcpy(&dst, &src, sizeof(T)) 却没检查 std::is_trivially_copyable_v<t></t> —— 这在调试模式下可能侥幸通过,上线后踩内存或崩溃。
std::is_trivially_copyable 和 std::is_pod 的区别在哪?
std::is_pod 已被 C++20 标记为弃用,它要求更严:除了平凡可复制,还必须是“平凡类型”(trivial)+“标准布局”(standard layout)。而 std::is_trivially_copyable 只管拷贝语义,不管内存布局是否跨平台兼容。
也就是说:
- 所有 POD 类型都是平凡可复制的,但反过来不成立
- 含
private成员或非公有继承的类,可能仍是平凡可复制,但不再是 POD - 若你只要求“能 memcpy”,就该用
std::is_trivially_copyable;若还需保证与 C 结构体二进制兼容(比如对接 C ABI),则额外检查std::is_standard_layout_v
别再用 std::is_pod 做判断依据了,它已过时且容易误导。
自定义类型怎么确保是平凡可复制?
关键不是“加什么”,而是“不做什么”:
- 不要声明任何构造函数(包括默认、拷贝、移动)——让编译器生成平凡版本
- 不要声明析构函数(即使空实现也不行;若真需要清理,考虑 RAII 封装而非在该类型里写逻辑)
- 不要有虚函数、虚基类
- 所有非静态数据成员和基类都必须是平凡可复制的
- 避免使用
std::unique_ptr、std::function等带控制块的类型作为成员
一个常见陷阱:给类型加了个 = default 的拷贝构造函数,以为这样就能保持平凡性——错。一旦显式声明(哪怕 defaulted),该构造函数就不再是“隐式声明”的,类型将失去“平凡拷贝构造”属性,从而整体不再是平凡可复制。
真正安全的做法:完全不写任何特殊成员函数,让编译器全自动合成。


















