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 起) | |||||||
属性 | - | (自 C++11 起) 任意数量的属性 | ||
constexpr
|
- | (C++17 起) 如果存在,该语句成为 constexpr if 语句 | ||
init-statement | - | (C++17 起) 可以是
注意,任何 init-statement 都必须以分号结尾。这就是为什么它通常被非正式地描述为表达式或声明后跟分号的原因。 | ||
条件 | - | 一个条件 | ||
statement-true | - | 如果 condition 产生 true,则执行的语句 | ||
statement-false | - | 如果 condition 产生 false,则执行的语句 | ||
复合语句 | - | 如果 if 语句在明确的常量求值上下文中求值 (如果 consteval 前有 ! 则不在这样的上下文中求值),则执行的复合语句 | ||
语句 | - | 如果 if 语句不在明确的常量求值上下文中求值 (如果 consteval 前有 ! 则在这样的上下文中求值),则执行的语句 (必须是复合语句,见下文) |
[编辑] 条件
|
(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() { // 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 起) |
[编辑] 注意
如果 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 }
如果通过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 中相同) 不评估条件,不执行第二个子语句(与 C 中相同) |
[编辑] 参阅
(C++20) |
检测调用是否发生在常量求值上下文内 (函数) |
if 语句 的C 文档
|