static_assert要求表达式为编译期常量,sizeof(t)仅在t为完整类型时有效;需结合alignof、constexpr函数、模板偏特化等确保布局与尺寸约束,避免前向声明或非字面类型导致失败。

static_assert 要求表达式必须是常量表达式
直接对模板参数用 sizeof(T) 是可行的,但关键在于:所有参与判断的值都得在编译期确定。比如你不能写 static_assert(sizeof(T) == 4, "...") 然后把 T 设为运行时才确定的类型——这根本过不了模板实例化阶段。真正能用的前提是 T 是一个具体、可名命的类型(如 int、MyStruct),且其尺寸不依赖于未定义行为或未完成类型。
常见错误现象:static_assert(sizeof(T) == 8) 在模板中报错 “expression did not evaluate to a constant”,往往是因为 T 是个前向声明的类(class X;),此时 sizeof(X) 非良构;或者用了 std::vector<t></t> 这类非字面类型的 sizeof——它本身不是编译期常量,但它的模板实参如果是完整类型,sizeof 就没问题。
检查结构体/类是否恰好是某尺寸(含填充控制)
很多场景下,你想确保某个自定义结构体按预期对齐和填充,比如用于网络协议或内存映射 I/O。这时仅靠 sizeof(T) 不够,还得确认它没被意外加 padding,或确认它满足特定对齐约束。
- 用
alignof(T)配合sizeof(T)一起断言,例如:static_assert(sizeof(MyMsg) == 16 && alignof(MyMsg) == 8, "MyMsg layout broken") - 若需强制紧凑布局,加
#pragma pack(1)或[[gnu::packed]]属性,但注意:不同编译器对 packed 的解释可能有差异,static_assert无法捕获这种跨平台不一致 - 避免依赖
offsetof做复杂布局断言——offsetof本身要求成员类型是标准布局(standard-layout),否则不是常量表达式,static_assert会拒掉
用 constexpr 函数封装更复杂的尺寸逻辑
当判断逻辑不止是“等于某数”,比如“是 4 的倍数”或“不大于 64 字节”,直接写在 static_assert 里可读性差,也难复用。这时候应提取成 constexpr 函数:
template<typename t>
constexpr bool is_power_of_two_size() {
constexpr auto s = sizeof(T);
return s != 0 && (s & (s - 1)) == 0;
}
<p>template<typename t>
struct Buffer {
static_assert(is_power_of_two_size<t>(), "T size must be power of two");
T data;
};</t></typename></p></typename>
注意:C++17 起支持变量模板,也可写成 template<typename t> constexpr bool is_32byte_aligned = (sizeof(T) % 32 == 0);</typename>,然后在 static_assert 中直接用。
模板特化 + static_assert 拦截非法参数组合
有时你不想让模板接受任意尺寸的类型,而是只允许一组预设尺寸(如只支持 1/2/4/8 字节整型)。这时可结合偏特化与 static_assert 实现白名单式检查:
template<typename t size_t n="sizeof(T)">
struct valid_size {};
<p>template<typename t>
struct valid_size<t> {};
template<typename t>
struct valid_size<t> {};
template<typename t>
struct valid_size<t> {};
template<typename t>
struct valid_size<t> {};</t></typename></t></typename></t></typename></t></typename></p>
<p>template<typename t>
struct MyType {
static_assert(valid_size<t>{}, "T must have size 1, 2, 4 or 8");
};</t></typename></p></typename>
这个技巧本质是利用 SFINAE 友好型偏特化触发编译失败,比单纯 static_assert(sizeof(T) == 1 || sizeof(T) == 2 || ...) 更易维护,也更容易给出清晰错误信息(取决于编译器是否显示特化匹配失败原因)。
容易忽略的一点:sizeof(void)、sizeof(function type) 是非法的,任何试图在模板中对它们求尺寸的操作都会导致硬错误,而不是 static_assert 失败——前者发生在模板解析阶段,后者在实例化之后。所以如果你的模板可能接收不完整类型,得先用 std::is_complete_v<t></t> 做前置保护。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











