if
statement
有条件地执行另一条语句。
用于需要根据条件执行代码的情况 ,或者 if 语句是否在显式常量求值上下文中被求值 (since C++23) 。
目录 |
语法
attr
(可选)
if
constexpr
(可选)
(
init-statement
(可选)
condition
)
statement-true
|
(1) | ||||||||
attr
(可选)
if
constexpr
(可选)
(
init-statement
(可选)
condition
)
statement-true
else
statement-false
|
(2) | ||||||||
attr
(可选)
if
!
(可选)
consteval
compound-statement
|
(3) | (C++23 起) | |||||||
attr
(可选)
if
!
(可选)
consteval
compound-statement
else
statement
|
(4) | (C++23 起) | |||||||
| attr | - | (since C++11) 任意数量的 属性 | ||
constexpr
|
- | (since C++17) 若存在,该语句将成为 constexpr if 语句 | ||
| init-statement | - |
(since C++17)
以下之一
注意:任何 init-statement 必须以分号结尾。因此常被非正式地描述为表达式或声明后接分号。 |
||
| condition | - | 一个 条件 | ||
| statement-true | - | 当 condition 结果为 true 时要执行的 语句 | ||
| statement-false | - | 当 condition 结果为 false 时要执行的语句 | ||
| compound-statement | - |
当
if
语句在
明显常量求值上下文
中被求值时要执行的
复合语句
(若在
consteval
前有
!
则指不在该上下文中求值时)
|
||
| statement | - |
当
if
语句不在明显常量求值上下文中求值时要执行的语句(必须是复合语句,参见
下文
)(若在
consteval
前有
!
则指在该上下文中求值时)
|
条件表达式
|
(since C++26) |
- 如果它能被语法解析为表达式,则被视为表达式。否则,它被视为声明 (非结构化绑定声明 (自 C++26 起) 。
当控制流到达条件语句时,该条件将产生一个值,该值用于确定控制流将转向哪个分支。
表达式
如果 condition 是一个表达式,其产生的值是该表达式在上下文中转换为 bool 类型后的值。如果该转换不合法,则程序也不合法。
声明
如果 condition 是一个简单声明,其产生的值是由决策变量(见下文)经上下文转换到 bool 类型的值。若该转换格式错误,则程序格式错误。
非结构化绑定声明
该声明具有以下限制:
- 语法上符合以下形式:
|
(C++11 前) |
|
(C++11 起) |
声明的决策变量即为所声明的变量。
结构化绑定声明该声明具有以下限制: 该声明的决策变量是由 声明引入的 虚构变量 e 。 |
(自 C++26 起) |
分支选择
如果 condition 的结果为 true ,则执行 statement-true 。
如果 if 语句的 else 部分存在且 condition 求值为 false ,则执行 statement-false 。
如果 if 语句存在 else 部分,且 statement-true 同样是一个 if 语句,那么该内层 if 语句也必须包含 else 部分(换言之,在嵌套的 if 语句中, else 会与最近一个尚未关联 else 的 if 语句相匹配)。
#include <iostream> int main() { // 带 else 子句的简单 if 语句 int i = 2; if (i > 2) std::cout << i << " is greater than 2\n"; else std::cout << i << " is not greater than 2\n"; // 嵌套 if 语句 int j = 1; if (i > 1) if (j > 2) std::cout << i << " > 1 and " << j << " > 2\n"; else // 此 else 属于 if (j > 2),而非 if (i > 1) std::cout << i << " > 1 and " << j << " <= 2\n"; // 声明语句可作为 dynamic_cast 的条件 struct Base { virtual ~Base() {} }; struct Derived : Base { void df() { std::cout << "df()\n"; } }; Base* bp1 = new Base; Base* bp2 = new Derived; if (Derived* p = dynamic_cast<Derived*>(bp1)) // 转换失败,返回 nullptr p->df(); // 未执行 if (auto p = dynamic_cast<Derived*>(bp2)) // 转换成功 p->df(); // 已执行 }
输出:
2 is not greater than 2 2 > 1 and 1 <= 2 df()
带初始化器的 if 语句若使用 init-statement ,则 if 语句等价于
或
除了由 init-statement 声明的名称(若 init-statement 是声明)和由 condition 声明的名称(若 condition 是声明)在同一作用域内,该作用域也是两个 statement s 的作用域。 std::map<int, std::string> m; std::mutex mx; extern bool shared_flag; // guarded by mx int demo() { if (auto it = m.find(10); it != m.end()) return it->second.size(); if (char buf[10]; std::fgets(buf, 10, stdin)) m[0] += buf; if (std::lock_guard lock(mx); shared_flag) { unsafe_ping(); shared_flag = false; } if (int s; int count = ReadBytesWithSignal(&s)) { publish(count); raise(s); } if (const auto keywords = {"if", "for", "while"}; std::ranges::any_of(keywords, [&tok](const char* kw) { return tok == kw; })) { std::cerr << "Token must not be a keyword\n"; } } |
(since C++17) | ||||||||||||||||||||||||||||||||||||||||||||||
Constexpr if以 if constexpr 开头的语句被称为 constexpr if 语句 。constexpr if 语句的所有子语句都是 控制流受限语句 。 在 constexpr if 语句中, 条件 必须是 上下文转换到 bool 类型的常量表达式 (C++23 前) 上下文转换到 bool 的表达式,且该转换是 常量表达式 (C++23 起) 。 如果 条件 产生 true ,则 false 语句 被丢弃(如果存在),否则 true 语句 被丢弃。 被丢弃语句中的 return 语句不参与函数返回类型推导: template<typename T> auto get_value(T t) { if constexpr (std::is_pointer_v<T>) return *t; // 对 T = int* 推导返回类型为 int else return t; // 对 T = int 推导返回类型为 int } 被丢弃语句可以 ODR 使用 未定义的变量: extern int x; // 不需要 x 的定义 int f() { if constexpr (true) return 0; else if (x) return x; else return -x; } 在模板外部,被丢弃语句会被完整检查。 if constexpr 不能替代 #if 预处理指令: void f() { if constexpr(false) { int i = 0; int *p = i; // 错误:即使在被丢弃语句中 } } 如果 constexpr if 语句出现在 模板化实体 内部,且实例化后 条件 不是 值依赖的 ,则当外围模板实例化时不会实例化被丢弃语句。 template<typename T, typename ... Rest> void g(T&& p, Rest&& ...rs) { // ... 处理 p if constexpr (sizeof...(rs) > 0) g(rs...); // 使用空实参列表时永远不会被实例化 } 实例化后条件保持值依赖的情况是嵌套模板: template<class T> void g() { auto lm = [=](auto p) { if constexpr (sizeof(T) == 1 && sizeof p == 1) { // 该条件在 g<T> 实例化后保持值依赖, // 这会影响隐式 lambda 捕获 // 该复合语句可能仅在 lambda 体实例化后被丢弃 } }; } 被丢弃语句不能对所有可能的特化都非良构: template<typename T> void f() { if constexpr (std::is_arithmetic_v<T>) // ... else { using invalid_array = int[-1]; // 非良构:对所有 T 都无效 static_assert(false, "Must be arithmetic"); // CWG2518 前非良构 } } 在实现 CWG 问题 2518 之前,此类全能语句的常用解决方案是使用始终为 false 的类型依赖表达式: template<typename> constexpr bool dependent_false_v = false; template<typename T> void f() { if constexpr (std::is_arithmetic_v<T>) // ... else { // CWG2518 前的解决方案 static_assert(dependent_false_v<T>, "Must be arithmetic"); } } typedef 声明 或 别名声明 (C++23 起) 可用作 constexpr if 语句的 初始化语句 以缩小类型别名的作用域。
|
(C++17 起) |
立即求值 if 语句以 if consteval 开头的语句被称为 立即求值 if 语句 。立即求值 if 语句的所有子语句都是 控制流受限语句 。 语句 必须是复合语句,即使它不是复合语句(因此会导致编译错误),它仍将被视为立即求值 if 语句的一部分:
运行此代码
constexpr void f(bool b) { if (true) if consteval {} else ; // 错误:不是复合语句 // else 不与外层 if 关联 } 如果立即求值 if 语句在 明显常量求值上下文 中被求值,则执行 复合语句 。否则,如果存在 语句 ,则执行该语句。 如果语句以 if ! consteval 开头,则 复合语句 和 语句 (如果有)都必须为复合语句。此类语句不被视为立即求值 if 语句,但等价于立即求值 if 语句:
立即求值 if 语句中的 复合语句 (或否定形式中的 语句 )处于 立即函数上下文 中,其中对立即函数的调用不必是常量表达式。
运行此代码
#include <cmath> #include <cstdint> #include <cstring> #include <iostream> constexpr bool is_constant_evaluated() noexcept { if consteval { return true; } else { return false; } } constexpr bool is_runtime_evaluated() noexcept { if not consteval { return true; } else { return false; } } consteval std::uint64_t ipow_ct(std::uint64_t base, std::uint8_t exp) { if (!base) return base; std::uint64_t res{1}; while (exp) { if (exp & 1) res *= base; exp /= 2; base *= base; } return res; } constexpr std::uint64_t ipow(std::uint64_t base, std::uint8_t exp) { if consteval // 使用编译期友好算法 { return ipow_ct(base, exp); } else // 使用运行时求值 { return std::pow(base, exp); } } int main(int, const char* argv[]) { static_assert(ipow(0, 10) == 0 && ipow(2, 10) == 1024); std::cout << ipow(std::strlen(argv[0]), 3) << '\n'; } |
(C++23 起) |
注释
如果 statement-true 或 statement-false 不是复合语句,则将其视为:
if (x) int i; // i 已不在作用域内
等同于
if (x) { int i; } // i 已超出作用域
由 condition 引入的名称作用域(若其为声明)是两条语句体共同组成的组合作用域:
if (int x = f()) { int x; // 错误:x 的重声明 } else { int x; // 错误:x 的重声明 }
如果通过
goto
或
longjmp
进入
statement-true
,则不会对
condition
进行求值,且不会执行
statement-false
。
|
(C++17 起)
(C++23 前) |
| 功能测试宏 | 值 | 标准 | 功能特性 |
|---|---|---|---|
__cpp_if_constexpr
|
201606L
|
(C++17) |
constexpr
if
|
__cpp_if_consteval
|
202106L
|
(C++23) |
consteval
if
|
关键词
if , else , constexpr , consteval
缺陷报告
以下行为变更缺陷报告被追溯应用于先前发布的C++标准。
| 缺陷报告 | 应用于 | 发布时的行为 | 正确行为 |
|---|---|---|---|
| CWG 631 | C++98 |
若通过标签进入第一条子语句
控制流是未指定的 |
不计算条件且不执行第二条
子语句(与C语言相同) |
参见
|
(C++20)
|
检测函数调用是否发生在常量求值上下文中
(函数) |
|
C 文档
关于
if
语句
|
|