有条件地执行另一条语句。

用于需要基于运行时或编译时条件执行的代码。

语法

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) - 任意数量的属性
条件 - 下列之一

- 按语境转换为 bool 的表达式
- 单个非数组变量的带花括号或等号初始化器声明
初始化语句(C++17) - 下列之一

- 一条表达式语句(可以是空语句;”)
- 一条简单声明,典型的是带初始化器的变量声明,但它可以声明任意多变量,或是一条分解声明


- 注意,任何 初始化语句 必然以分号 ; 结束,这是它常被非正式地描述成后随分号的表达式或声明的原因。
true分支语句 - 任何语句(常为复合语句),当 条件 求值为 true 时执行
false分支语句 - 任何语句(常为复合语句),当 条件 求值为 false 时执行

解释

若 条件 在转换到 bool 后产生 true,则执行 true分支语句。

若 if 语句的 else 部分存在,且 条件 在转换到 bool 后产生 false,则执行 false分支语句。

在 if 语句的第二形式(包含 else)中,若 true分支语句 亦是 if 语句,则内层 if 语句必须也含有 else 部分(换言之,嵌套 if 语句中,else 关联到最近的尚未有 else 的 if)。

运行此代码

  1. #include <iostream>
  2.  
  3. int main() {
  4. // 带 else 子句的简单 if 语句
  5. int i = 2;
  6. if (i > 2) {
  7. std::cout << i << " 大于 2\n";
  8. } else {
  9. std::cout << i << " 不大于 2\n";
  10. }
  11.  
  12. // 嵌套 if 语句
  13. int j = 1;
  14. if (i > 1)
  15. if (j > 2)
  16. std::cout << i << " > 1 且 " << j << " > 2\n";
  17. else // 此 else 属于 if (j > 2), 而不是 if (i > 1)
  18. std::cout << i << " > 1 且 " << j << " <= 2\n";
  19.  
  20. // 以下声明可用于含 dynamic_cast 的条件
  21. struct Base {
  22. virtual ~Base() {}
  23. };
  24. struct Derived : Base {
  25. void df() { std::cout << "df()\n"; }
  26. };
  27. Base* bp1 = new Base;
  28. Base* bp2 = new Derived;
  29.  
  30. if (Derived* p = dynamic_cast<Derived*>(bp1)) // 转型失败,返回 nullptr
  31. p->df(); // 不执行
  32.  
  33. if (auto p = dynamic_cast<Derived*>(bp2)) // 转型成功
  34. p->df(); // 执行
  35. }

输出:

  1. 2 不大于 2
  2. 2 > 1 1 <= 2
  3. df()


#### 带初始化器的 if 语句


若使用 初始化语句,则 if 语句等价于


{

- 初始化语句
- if constexpr(可选) ( 条件 )
- true分支语句

}

{

- 初始化语句
- if constexpr(可选) ( 条件 )
- true分支语句
- else
- false分支语句

}

但 初始化语句 所声明的名字(若 初始化语句 是声明)和 条件 所声明的名字(若 条件 是声明)处于同一作用域中,同时也是两条 语句 所在的作用域。

  1. std::map<int, std::string> m;
  2. std::mutex mx;
  3. extern bool shared_flag; // 由 mx 保证
  4. int demo() {
  5. if (auto it = m.find(10); it != m.end()) { return it->second.size(); }
  6. if (char buf[10]; std::fgets(buf, 10, stdin)) { m[0] += buf; }
  7. if (std::lock_guard lock(mx); shared_flag) { unsafe_ping(); shared_flag = false; }
  8. if (int s; int count = ReadBytesWithSignal(&s)) { publish(count); raise(s); }
  9. if (auto keywords = {"if", "for", "while"};
  10. std::any_of(keywords.begin(), keywords.end(),
  11. [&s](const char* kw) { return s == kw; })) {
  12. std::cerr << "Token 不能是关键词\n");
  13. }
  14. }

(C++17 起)



#### constexpr if


if constexpr 开始的语句被称为constexpr if 语句

在 constexpr if 语句中,条件 的值必须是可按语境转换到 bool 类型的经转换常量表达式。若其值为 true,则舍弃 false分支语句(若存在),否则舍弃 true分支语句。

被舍弃语句中的 return 语句不参与函数返回类型推导:




  1. 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 式使用未定义的变量




  1. extern int x; // 不需要 x 的定义
    int f() {
    if constexpr (true)
    return 0;
    else if (x)
    return x;
    else
    return -x;
    }




若 constexpr if 语句出现于模板实体内,且若 条件 在实例化后不是值待决的,则外围模板被实例化时不会实例化被舍弃语句。




  1. template<typename T, typename Rest>
    void g(T&& p, Rest&& rs) {
    // … 处理 p
    if constexpr (sizeof…(rs) > 0)
    g(rs…); // 始终不会对空实参列表实例化。
    }




在模板外,被舍弃语句受到完整的检查。if constexpr 不是 #if 预处理指令的替代品:




  1. void f() {
    if constexpr(false) {
    int i = 0;
    int *p = i; // 在被舍弃语句中仍为错误
    }
    }




注意:实例化后仍为值待决的一个例子是嵌套模板,例如




  1. template<class T> void g() {
    auto lm = {
    if constexpr (sizeof(T) == 1 && sizeof p == 1) {
    // 此条件在 g<T> 实例化后仍为值待决的
    }
    };
    }




注意:被舍弃语句不能对所有特化均非良构:




  1. template <typename T>
    void f() {
    if constexpr (std::is_arithmetic_v<T>)
    // …
    else
    static_assert(false, "必须是算术类型"); // 非良构:该语句对于所有 T 都非法
    }




对这种万应语句的常用变通方案,是一条始终为 false 的类型待决表达式:




  1. 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 目标case 标号和 default:),只能在同一子语句中(由 switchgoto)引用。
(C++17 起)

注解

若 true分支语句 或 false分支语句 不是复合语句,则按如同是复合语句一样处理:

  1. if (x)
  2. int i;
  3. // i 不再在作用域中

与下面相同

  1. if (x) {
  2. int i;
  3. } // i 不再在作用域中

条件 若是声明,则其所引入的名字的作用域是两个语句体的合并作用域:

  1. if (int x = f()) {
  2. int x; // 错误:重复声明了 x
  3. } else {
  4. int x; // 错误:重复声明了 x
  5. }

若通过 goto 或 longjmp 进入 true分支语句,则不执行 false分支语句。
(C++14 起)

不允许 switch 和 goto 跳入 constexpr if 语句的分支。
(C++17 起)

关键词

if,else,constexpr