直接用 std::is_trivially_default_constructible::value(c++11起),编译期返回true当且仅当t拥有平凡默认构造函数;注意它判断的是“是否平凡”,而非“是否存在默认构造函数”,且受成员、基类、虚函数等严格约束。

怎么用 std::is_trivially_default_constructible 判断?
直接用 std::is_trivially_default_constructible<t>::value</t>(C++11 起),它在编译期返回 true 当且仅当 T 拥有平凡默认构造函数。注意:不是“有没有默认构造函数”,而是“这个默认构造函数是否平凡”。
常见误用是拿它测自定义构造函数——只要类声明了任何构造函数(哪怕带默认参数),T 就不再有隐式声明的默认构造函数,自然谈不上“平凡默认构造”。此时该 trait 一定为 false。
-
struct A { int x; };→true(隐式、平凡) -
struct B { B() = default; int x; };→true(显式 defaulted,仍平凡) -
struct C { C() {} int x; };→false(用户提供的非 defaulted 实现,不平凡) -
struct D { D(int = 0) {} };→false(无默认构造函数,trait 不满足前提)
为什么 = default 有时也不够?
= default 只保证“语法上平凡”,但语义上是否真能平凡构造,还取决于成员和基类。只要任意非静态数据成员或基类没有平凡默认构造函数,整个类就失去平凡性。
例如:
struct NonTrivial {
NonTrivial() { } // 用户定义,不平凡
};
struct Bad : NonTrivial {
int x;
Bad() = default; // 这里 = default 无效:基类构造不平凡 → Bad 不平凡
};
- 基类、成员类型都必须满足
std::is_trivially_default_constructible - 即使所有成员都是 POD 类型,若存在虚函数或虚基类,也会破坏平凡性(因为需要初始化虚表指针)
- 静态成员不影响平凡性判断
运行时检查没意义,别想绕过编译期
这个性质完全由类定义决定,和对象值、内存布局无关。试图在运行时“检测某个对象是不是被平凡构造”是错误方向——std::is_trivially_default_constructible 是编译期常量表达式,不能用于变量或运行时类型(比如 auto 推导后判断)。
- 错误:
auto obj = T{}; static_assert(std::is_trivially_default_constructible<decltype>::value);</decltype>——decltype(obj)是T,没问题;但若写成decltype(foo())且foo()返回T&&,就会变成引用类型,trait 直接失效(引用类型不适用) - 更隐蔽的坑:模板中未约束类型,导致 trait 在某些特化下求值为
false,但代码仍试图做位拷贝(如memcpy)——这会引发未定义行为 - 正确姿势:在模板头部用
static_assert或std::enable_if显式约束
和 std::is_trivial 的关系容易混淆
std::is_trivially_default_constructible 只管默认构造函数是否平凡;而 std::is_trivial 要求全部:默认构造、析构、拷贝/移动,全都平凡,且不能有虚函数、虚基类、非平凡成员等。前者是后者的必要但不充分条件。
-
struct X { X() = default; ~X() = delete; };→is_trivially_default_constructible是true,但is_trivial是false - 如果你要安全地
memset或memcpy整个对象,必须用std::is_trivial,而不是只看构造 - 很多序列化库(如 flatbuffers)依赖
std::is_trivial,而非单独的构造 trait
真正难处理的是那些看似简单、但因一个 std::string 成员或虚继承就全盘失效的类——这种细节不报错,只在 memcpy 后崩溃或读到垃圾值。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











