编译期判断应优先于运行期,如type_trait、if constexpr和concept等零开销机制用于静态约束;仅当面对无法穷举的多态对象(如插件系统)时,才需dynamic_cast或typeid。

编译期判断能搞定的,别拖到运行期
绝大多数类型相关决策,比如“这个类型有没有begin()”“它是否是整数”“能不能隐式转成double”,都应该在编译期完成。因为编译期判断不产生任何运行时开销,还能让错误提前暴露——模板实例化失败比程序跑一半崩溃好 debug 多了。
常见误用是:看到 typeid 或 dynamic_cast 就上,结果发现本该在头文件里静态约束的接口,却要靠运行时查虚函数表才能确认。这不仅慢(现代 CPU 对间接跳转预测不准),还掩盖了设计缺陷:如果连类型兼容性都要等运行时才知道,说明模板契约没写清楚。
-
if constexpr适合分支逻辑依赖模板参数,且两侧代码不能共存(比如一边调.c_str(),一边调.data()) -
std::is_integral_v<t></t>这类type_trait是零成本判断,直接用于static_assert或 SFINAE - 别用
typeid判断模板参数——它返回的是运行时类型信息,而模板参数在编译期就已确定
运行期判断只在多态对象真实存在时才必要
运行期类型判断不是“备选方案”,而是特定场景下的唯一选择:你手上只有基类指针或引用,且子类类型无法在编译期穷举(比如插件系统、序列化反解、GUI 控件树遍历)。这时候 dynamic_cast 和 typeid 才有意义。
但要注意:dynamic_cast 要求目标类型有虚函数,否则编译不过;typeid 对非多态类型返回的是静态类型,和 decltype 效果一致,毫无意义。
立即学习“C++免费学习笔记(深入)”;
- 对普通变量或栈对象直接用
typeid(x),得到的是其声明类型,不是实际类型 - 用
dynamic_cast前先确认指针非空,否则返回nullptr(引用则抛异常) - 避免在 hot path(如循环体内)反复做
dynamic_cast——考虑用虚函数替代,或把类型分发逻辑提到外层
if constexpr 和普通 if 混用会出什么问题
最典型的问题是:你以为加了 if constexpr 就能屏蔽非法代码,结果编译器还是报错。原因通常是条件表达式本身无法在编译期求值,比如用了非常量变量、未定义的模板成员、或依赖运行时值的表达式。
if constexpr 的条件必须是常量表达式(constexpr context 下可求值),否则退化为普通 if,分支不会被丢弃,非法代码仍参与语义分析。
- 错误写法:
if constexpr (sizeof(T) > 4)✅ 可行;if constexpr (x > 0)❌x不是常量表达式 - 模板内访问嵌套类型前,先用
std::is_detected_v或requires检查,再进if constexpr分支 - C++20 起优先用
requires约束模板参数,比if constexpr更早拦截非法实例化
容易被忽略的性能与可维护性陷阱
编译期判断不是银弹。过度使用模板元编程或深度嵌套的 if constexpr 会让编译时间飙升,尤其在头文件中暴露复杂 trait 检查时,每个包含它的 TU 都要重新实例化一遍。
另一个隐形成本是错误信息质量:一个 static_assert 失败可能只告诉你 “T 不满足 Container 概念”,但没说缺哪个成员函数;而运行时 dynamic_cast 失败至少还能打印出类型名。
- 对高频使用的类型判断逻辑,封装成命名清晰的
concept(C++20)或 trait 别名,别在每个函数里重复写std::is_same_v堆砌 - 避免在模板中用
typeid+std::string比较做 dispatch——字符串比较是运行期行为,且不可内联 - 跨模块传递类型信息时,优先用编译期机制(如
std::variant+std::visit),而非靠运行时dynamic_cast链式查询


















