if
语句
有条件地执行另一个语句。
用于需要根据条件执行代码的情况,或者 if 语句是否在明显常量求值上下文中求值(从 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 | - | (从 C++11 开始) 任意数量的 属性 | ||
constexpr
|
- | (从 C++17 开始) 如果存在,该语句将成为 constexpr if 语句 | ||
init-statement | - | (从 C++17 开始) 其中之一
注意,任何 init-statement 必须以分号结尾。这就是为什么它通常非正式地描述为表达式或声明后跟分号的原因。 | ||
condition | - | 一个 条件 | ||
statement-true | - | 如果 condition 产生 true,则要执行的 语句 | ||
statement-false | - | 如果 condition 的结果是 false,则执行的语句。 | ||
compound-statement | - | 如果 if 语句在 显式常量求值上下文 中求值(或在 ! 位于 consteval 之前的情况下,不在这种上下文中求值),则执行的 复合语句。 | ||
statement | - | 如果 if 语句不在显式常量求值上下文(或在 ! 位于 consteval 之前的情况下,在该上下文中求值),则执行的语句(必须为复合语句,参见 下方)。 |
[edit] 条件
|
(自 C++26 起) |
- 如果它可以语法解析为表达式或声明(它不是结构化绑定声明)(自 C++26 起),则它被解释为后者。
当控制流到达条件时,条件将产生一个值,该值用于确定控制流将进入哪个分支。
[edit] 表达式
如果 condition 是表达式,则它产生的值为表达式上下文转换为 bool 的值。如果该转换是非法的,则程序是非法的。
[edit] 声明
如果 condition 是简单声明,则它产生的值为决策变量(参见下方)上下文转换为 bool 的值。如果该转换是非法的,则程序是非法的。
[edit] 非结构化绑定声明
该声明具有以下限制:
- 它只有一个 声明符。
- 声明符不能指定 函数 或 数组。
- 声明符必须具有 初始化器,该初始化器不能为语法 (3)。
- 声明说明符序列 只能包含类型说明符 和 constexpr(自 C++11 起),并且不能定义 类 或 枚举。
该声明的决策变量是声明的变量。
结构化绑定声明该声明具有以下限制: 该声明的决策变量是声明 引入 的虚拟变量 e。 |
(自 C++26 起) |
[edit] 分支选择
如果 condition 的结果是 true,则执行 statement-true。
如果 if 语句存在 else 部分,并且 condition 的结果是 false,则执行 statement-false。
如果 if 语句存在 else 部分,并且 statement-true 也是 if 语句,则该内部 if 语句也必须包含 else 部分(换句话说,在嵌套的 if 语句中,else 与最接近的 if 相关联,该 if 尚未与 else 相关联)。
#include <iostream> int main() { // simple if-statement with an else clause int i = 2; if (i > 2) std::cout << i << " is greater than 2\n"; else std::cout << i << " is not greater than 2\n"; // nested if-statement int j = 1; if (i > 1) if (j > 2) std::cout << i << " > 1 and " << j << " > 2\n"; else // this else is part of if (j > 2), not of if (i > 1) std::cout << i << " > 1 and " << j << " <= 2\n"; // declarations can be used as conditions with 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)) // cast fails, returns nullptr p->df(); // not executed if (auto p = dynamic_cast<Derived*>(bp2)) // cast succeeds p->df(); // executed }
输出
2 is not greater than 2 2 > 1 and 1 <= 2 df()
if 语句与初始化器如果使用 init-statement,则 if 语句等效于
或
不同之处在于,由 init-statement(如果 init-statement 是声明)声明的名称和由 condition(如果 condition 是声明)声明的名称在同一个作用域中,该作用域也是两个 statement 的作用域。 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"; } } |
(自 C++17 起) |
Constexpr if以 if constexpr 开头的语句称为constexpr if 语句。constexpr if 语句的所有子语句都是 控制流受限语句。 在 constexpr if 语句中,condition 必须是类型为 bool 的 上下文转换的常量表达式(直到 C++23)上下文转换为 bool 的表达式,其中转换是 常量表达式(自 C++23 起)。 如果 condition 的结果是 true,则 statement-false 被丢弃(如果存在),否则,statement-true 被丢弃。 被丢弃的语句中的 return 语句不参与函数返回类型推导。 template<typename T> auto get_value(T t) { if constexpr (std::is_pointer_v<T>) return *t; // deduces return type to int for T = int* else return t; // deduces return type to int for T = int } 被丢弃的语句可以 ODR 使用 未定义的变量。 extern int x; // no definition of x required 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; // Error even though in discarded statement } } 如果 constexpr if 语句出现在 模板实体 内部,并且如果 condition 在实例化后不是 值依赖的,则当封闭模板被实例化时,被丢弃的语句不会被实例化。 template<typename T, typename ... Rest> void g(T&& p, Rest&& ...rs) { // ... handle p if constexpr (sizeof...(rs) > 0) g(rs...); // never instantiated with an empty argument list } 在实例化后,条件仍然是值依赖的,是一个嵌套模板。 template<class T> void g() { auto lm = [=](auto p) { if constexpr (sizeof(T) == 1 && sizeof p == 1) { // this condition remains value-dependent after instantiation of g<T>, // which affects implicit lambda captures // this compound statement may be discarded only after // instantiation of the lambda body } }; } 对于每种可能的特殊化,被丢弃的语句不能是非法的。 template<typename T> void f() { if constexpr (std::is_arithmetic_v<T>) // ... else { using invalid_array = int[-1]; // ill-formed: invalid for every T static_assert(false, "Must be arithmetic"); // ill-formed before 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 { // workaround before CWG2518 static_assert(dependent_false_v<T>, "Must be arithmetic"); } } 可以使用 typedef 声明 或 别名声明(自 C++23 起) 作为 constexpr if 语句的 init-statement 来缩小别名的作用域。
|
(自 C++17 起) |
Consteval if以 if consteval 开头的语句称为consteval if 语句。consteval if 语句的所有子语句都是 控制流受限语句。 statement 必须是复合语句,即使它不是复合语句(因此会导致编译错误),它也将被视为 consteval if 语句的一部分。 运行此代码 constexpr void f(bool b) { if (true) if consteval {} else ; // error: not a compound-statement // else not associated with outer if } 如果在 显式常量求值上下文 中求值 consteval if 语句,则执行 compound-statement。否则,如果存在,则执行 statement。 如果语句以 if !consteval 开头,则 compound-statement 和 statement(如果有)都必须是复合语句。这些语句不被视为 consteval if 语句,但等效于 consteval if 语句。
consteval if 语句中的 compound-statement(或否定形式中的 statement)处于 立即函数上下文 中,在该上下文中,对立即函数的调用不必是常量表达式。 运行此代码 #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 // use a compile-time friendly algorithm { return ipow_ct(base, exp); } else // use runtime evaluation { 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 开始) |
[edit] 注释
如果 statement-true 或 statement-false 不是复合语句,则将其视为
if (x) int i; // i is no longer in scope
与以下相同
if (x) { int i; } // i is no longer in scope
如果 condition 是声明,则它引入的名称的作用域是两个语句体的组合作用域。
if (int x = f()) { int x; // error: redeclaration of x } else { int x; // error: redeclaration of x }
如果 statement-true 通过 goto
或 longjmp 进入,则不会对 condition 求值,也不会执行 statement-false。
在 条件 为 constexpr if 语句中,不允许使用内置转换,除非是不进行 收窄 的 整型转换 到 bool 类型。 |
(自 C++17 起) (直到 C++23) |
功能测试宏 | 值 | Std | 特性 |
---|---|---|---|
__cpp_if_constexpr |
201606L | (C++17) | constexpr if |
__cpp_if_consteval |
202106L | (C++23) | consteval if |
[编辑] 关键字
if, else, constexpr, consteval
[编辑] 缺陷报告
以下行为更改的缺陷报告被追溯应用到先前发布的 C++ 标准。
DR | 应用于 | 已发布的行为 | 正确行为 |
---|---|---|---|
CWG 631 | C++98 | 如果第一个子语句通过标签到达,则控制流未指定 条件不被评估,第二个子语句不被执行(与 C 中相同) |
the condition is not evaluated and the second substatement is not executed (same as in C) |
[编辑] 另请参阅
(C++20) |
检测调用是否发生在常量评估上下文中 (函数) |
C 文档 for if 语句
|