constexpr if

constexpr if
苏丙榅1. 什么是 constexpr if
简单来说,constexpr if 是 C++17 引入的一个编译期判断语句。它的作用是告诉编译器:在编译代码的时候,就根据这个条件决定保留哪一段代码,扔掉哪一段代码。
打个通俗的比方,想象你在打包行李(编译代码):
- 普通的
if语句:就像把两把伞都放进了箱子,一把是遮阳伞,一把是雨伞。等到了目的地(程序运行时),看天气决定用哪一把。两把伞都得背着,箱子很重。 constexpr if语句:就像在打包前看了一下天气预报。如果目的地是大晴天,就只把遮阳伞放进去,雨伞直接扔一边不带了。这样箱子(可执行文件)更轻,而且在目的地绝对不会误拿雨伞。
在 C++17 之前,如果用了模板(泛型),编写函数时往往需要写很多在这个特定模板类型下根本用不到的代码,或者会因为类型不同而导致编译错误。
constexpr if 允许我们将那些对当前类型来说非法的代码直接在编译阶段剔除掉,于是编译器就不会检查那些被剔除的代码的语法正确性。
它的写法和普通 if 几乎一样,只是开头加了一个 constexpr 关键字:
1 | if constexpr (条件) |
- 注意:
constexpr在if的后面,不是前面! - 普通
if:条件在运行时判断。两个分支(if-else)都会被编译,只是运行时只走一个。 constexpr if:条件在编译期判断。如果不满足条件,那个分支的代码根本不会被编译。
| 特性 | 传统 if |
constexpr if |
|---|---|---|
| 判断时间 | 运行时 | 编译时 |
| 性能影响 | 需要判断分支 | 零运行时开销 |
| 代码编译 | 所有分支都编译 | 只编译真分支 |
| 适用场景 | 普通条件判断 | 模板、编译时常量 |
2. 为什么需要它
关于constexpr if 的使用,最常见的场景就是处理不同类型的不同行为。比如,我们想写一个函数,既可以处理 int(数字),也可以处理 std::string(文字)。我们想要:
- 如果是数字,就做加法。
- 如果是字符串,就输出长度。
如果不使用 constexpr if,代码可能会报错,因为不能对字符串做加法(在某种语境下),或者代码逻辑会很乱。
2.1 基础用法
1 |
|
std::is_integral_v 是 C++17 中引入的一个模板变量,用于在编译时检查一个类型是否为整数类型。
std::is_integral_v<T>对以下类型返回 true:
boolchar,signed char,unsigned charchar8_t(C++20)char16_t,char32_t,wchar_tshort,unsigned shortint,unsigned intlong,unsigned longlong long,unsigned long long
2.2 让非法代码变得合法
看下面的代码,我们尝试获取一个值的 .size()。
1 |
|
在函数模板printCount中,如果没有 constexpr if,直接写 container.size(),那么 printCount(s) 这行代码就会编译报错,因为 MyStruct 没有 .size() 方法。有了constexpr if,编译器在编译 printCount(s) 时,直接把 container.size() 那段代码删掉了,所以不会报错!
3. constexpr if 不是万能的
constexpr if 绝不是万能药,如果用得不好,反而会写出看似正确实则挂了的代码。
下面的代码是一个错误示例:
1 |
|
使用constexpr if的时候它的条件要求非常严格,总结为一句话:条件必须是“编译期常量”且必须是“上下文转换为 bool 值”的表达式。这意味着编译器在编译代码时,必须能够直接计算出这个条件的结果是 true 还是 false,而不能依赖运行时的变量。
- 必须是常量表达式
- 允许
constexpr变量:if constexpr (N > 0)enum枚举值:if constexpr (Color::Red == 0)sizeof/alignof:if constexpr (sizeof(int) == 4)- Type Traits (最常用):
if constexpr (std::is_pointer_v<T>)、if constexpr (std::is_integral_v<T>)。
- 不允许
- 普通变量:
int x = 5; if constexpr (x > 0),x 的值虽然已知,但不是常量表达式。 - 函数参数:
void func(int n) { if constexpr (n > 0) ... },n 是运行时传入的。 - 运行时函数结果:
if constexpr (std::time(nullptr) > 0),函数返回值不是常量。
- 普通变量:
- 允许
- 必须能隐式转换为
boolif constexpr (10)->trueif constexpr (nullptr)->falseif constexpr (std::is_integral_v<int>)->true
constexpr if 是一把锋利的手术刀,用来切除模板中的类型差异,但它不是锤子,不能到处乱砸。













