C++ 参考手册
- C++11
- C++14
- C++17
- C++20
- C++ 编译器支持情况表
- 独立与宿主实现
- C++ 语言
- 变量模板(C++14 起)
- 整数字面量
- 聚合初始化
- 比较运算符
- 默认比较(C++20 起)
- 转义序列
- for 循环
- while 循环
- 用户定义转换
- SFINAE
- 主函数
- ASCII 码表
- 标识符
- 类型
- 内存模型
- 对象
- 基本概念
- 表达式
- 声明
- 初始化
- 函数
- 语句
- 类
- 运算符重载
- 模板
- 异常
- 事务性内存
- 占位符类型说明符 (C++11 起)
- decltype 说明符
- 函数声明
- final 说明符 (C++11 起)
- override 说明符(C++11 起)
- 引用声明
- 移动构造函数
- 移动赋值运算符
- 枚举声明
- constexpr 说明符(C++11 起)
- 列表初始化 (C++11 起)
- 构造函数与成员初始化器列表
- using 声明
- nullptr,指针字面量
- 基础类型
- 类型别名,别名模版 (C++11 起)
- 形参包
- 联合体声明
- 字符串字面量
- 用户定义字面量 (C++11 起)
- 属性说明符序列(C++11 起)
- Lambda 表达式 (C++11 起)
- noexcept 说明符 (C++11 起)
- noexcept 运算符 (C++11 起)
- alignof 运算符(C++11 起)
- alignas 说明符 (C++11 起)
- 存储类说明符
- 基于范围的 for 循环 (C++11 起)
- static_assert 声明
- 隐式转换
- 代用运算符表示
- 自增/自减运算符
- 折叠表达式(C++17 起)
- 类模板实参推导(C++17 起)
- 模板形参与模板实参
- if 语句
- inline 说明符
- 结构化绑定声明 (C++17 起)
- switch 语句
- 字符字面量
- 命名空间
- 求值顺序
- 复制消除
- consteval 说明符 (C++20 起)
- constinit 说明符 (C++20 起)
- 协程 (C++20)
- 模块 (C++20 起)
- 约束与概念 (C++20 起)
- new 表达式
- do-while 循环
- continue 语句
- break 语句
- goto 语句
- return 语句
- 动态异常说明
- throw 表达式
- try 块
- 命名空间别名
- 类声明
- cv(const 与 volatile)类型限定符
- 默认初始化
- 值初始化(C++03 起)
- 零初始化
- 复制初始化
- 直接初始化
- 常量初始化
- 引用初始化
- 值类别
- C++ 运算符优先级
- 布尔字面量
- 浮点字面量
- typedef 说明符
- 显式类型转换
- static_cast 转换
- dynamic_cast 转换
- const_cast 转换
- reinterpret_cast 转换
- delete 表达式
- 构造函数与成员初始化器列表
- this 指针
- 访问说明符
- 友元声明
- virtual 函数说明符
- explicit 说明符
- 静态成员
- 默认构造函数
- 复制构造函数
- 复制赋值运算符
- 析构函数
- 类模板
- 函数模板
- 显式(全)模板特化
- 汇编声明
- C++ 的历史
- 作用域
- 生存期
- 定义与单一定义规则(ODR)
- 名字查找
- 有限定的名字查找
- 无限定的名字查找
- 如同规则
- 未定义行为
- 翻译阶段
- 常量表达式
- 赋值运算符
- 算术运算符
- 逻辑运算符
- 成员访问运算符
- 其他运算符
- sizeof 运算符
- typeid 运算符
- 指针声明
- 数组声明
- 语言链接
- 详述类型说明符
- 默认实参
- 变长实参
- 实参依赖查找
- 重载决议
- 重载函数的地址
- 注入类名
- 非静态数据成员
- 非静态成员函数
- 嵌套类
- 派生类
- 空基类优化
- 抽象类
- 位域
- 转换构造函数
- 成员模板
- 模板实参推导
- 部分模板特化
- sizeof... 运算符
- 待决名
- 函数 try 块
- 扩充命名空间 std
- 字母缩写
- RAII
- 三/五/零之法则
- PImpl
- 零开销原则
- 类型
- 隐式转换
- 注释
- C++ 关键词
- 预处理器
- C++ 标准库头文件
- 具名要求
- 功能特性测试 (C++20)
- 工具库
- 类型支持(基本类型、RTTI、类型特性)
- 概念库 (C++20)
- 错误处理
- 动态内存管理
- 日期和时间工具
- 字符串库
- 容器库
- 迭代器库
- 范围库 (C++20)
- 算法库
- 数值库
- 输入/输出库
- 文件系统库
- 本地化库
- 正则表达式库
- 原子操作库
- 线程支持库
- 实验性 C++ 特性
- 有用的资源
- 索引
- std 符号索引
- 协程支持 (C++20)
- C++ 关键词
if 语句
有条件地执行另一条语句。
用于需要基于运行时或编译时条件执行的代码。
语法
attr(可选) if ( 条件 ) true分支语句
|
(1) | (C++17 前) | |||||||
attr(可选) if constexpr (可选) ( 初始化语句(可选) 条件 ) true分支语句
|
(1) | (C++17 起) | |||||||
attr(可选) if ( 条件 ) true分支语句 else false分支语句
|
(2) | (C++17 前) | |||||||
attr(可选) if constexpr (可选) ( 初始化语句(可选) 条件 ) true分支语句 else false分支语句
|
(2) | (C++17 起) | |||||||
attr(C++11) | - | 任意数量的属性 |
条件 | - | 下列之一 |
初始化语句(C++17) | - | 下列之一
|
true分支语句 | - | 任何语句(常为复合语句),当 条件 求值为 true 时执行
|
false分支语句 | - | 任何语句(常为复合语句),当 条件 求值为 false 时执行
|
解释
若 条件 在转换到 bool 后产生 true,则执行 true分支语句。
若 if 语句的 else 部分存在,且 条件 在转换到 bool 后产生 false,则执行 false分支语句。
在 if 语句的第二形式(包含 else)中,若 true分支语句 亦是 if 语句,则内层 if 语句必须也含有 else 部分(换言之,嵌套 if 语句中,else 关联到最近的尚未有 else 的 if)。
#include <iostream> int main() { // 带 else 子句的简单 if 语句 int i = 2; if (i > 2) { std::cout << i << " 大于 2\n"; } else { std::cout << i << " 不大于 2\n"; } // 嵌套 if 语句 int j = 1; if (i > 1) if (j > 2) std::cout << i << " > 1 且 " << j << " > 2\n"; else // 此 else 属于 if (j > 2), 而不是 if (i > 1) std::cout << i << " > 1 且 " << 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 不大于 2 2 > 1 且 1 <= 2 df()
带初始化器的 if 语句若使用 初始化语句,则 if 语句等价于
或
但 初始化语句 所声明的名字(若 初始化语句 是声明)和 条件 所声明的名字(若 条件 是声明)处于同一作用域中,同时也是两条 语句 所在的作用域。 std::map<int, std::string> m; std::mutex mx; extern bool shared_flag; // 由 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 (auto keywords = {"if", "for", "while"}; std::any_of(keywords.begin(), keywords.end(), [&s](const char* kw) { return s == kw; })) { std::cerr << "Token 不能是关键词\n"); } } |
(C++17 起) |
constexpr if以 在 constexpr if 语句中,条件 的值必须是可按语境转换到 弃用语句中的 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; } 若 constexpr if 语句出现于模板实体内,且若 条件 在实例化后不是值待决的,则外围模板被实例化时不会实例化弃用语句。 template<typename T, typename ... Rest> void g(T&& p, Rest&& ...rs) { // ... 处理 p if constexpr (sizeof...(rs) > 0) g(rs...); // 始终不会对空实参列表实例化。 } 在模板外,弃用语句受到完整的检查。 void f() { if constexpr(false) { int i = 0; int *p = i; // 在弃用语句中仍为错误 } } 注意:实例化后仍为值待决的一个例子是嵌套模板,例如 template<class T> void g() { auto lm = [](auto p) { if constexpr (sizeof(T) == 1 && sizeof p == 1) { // 此条件在 g<T> 实例化后仍为值待决的 } }; } 注意:弃用语句不能对所有特化均非良构: template <typename T> void f() { if constexpr (std::is_arithmetic_v<T>) // ... else static_assert(false, "必须是算术类型"); // 非良构:该语句对于所有 T 都非法 } 对这种万应语句的常用变通方案,是一条始终为 false 的类型待决表达式: template<class T> struct dependent_false : std::false_type {}; template <typename T> void f() { if constexpr (std::is_arithmetic_v<T>) // ... else static_assert(dependent_false<T>::value, "必须是算术类型"); // ok } 在 constexpr if 的子语句中出现的标号(goto 目标、 |
(C++17 起) |
注解
若 true分支语句 或 false分支语句 不是复合语句,则按如同是复合语句一样处理:
if (x) int i; // i 不再在作用域中
与下面相同
if (x) { int i; } // i 不再在作用域中
条件 若是声明,则其所引入的名字的作用域是两个语句体的合并作用域:
if (int x = f()) { int x; // 错误:重复声明了 x } else { int x; // 错误:重复声明了 x }
若通过 goto 或 longjmp 进入 true分支语句,则不执行 false分支语句。 |
(C++14 起) |
不允许 switch 和 goto 跳入 constexpr if 语句的分支。 |
(C++17 起) |