if 语句

来自cppreference.com
< cpp‎ | language


 
 
 
 

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

用于需要基于条件或者 if 语句是否在明显常量求值语境下求值(C++23 起)执行的代码。

目录

[编辑]语法

属性 (可选)ifconstexpr(可选)(初始化语句 (可选)条件)true分支语句 (1)
属性 (可选)ifconstexpr(可选)(初始化语句 (可选)条件)true分支语句elsefalse分支语句 (2)
属性 (可选)if!(可选)consteval复合语句 (3) (C++23 起)
属性 (可选)if!(可选)consteval复合语句else语句 (4) (C++23 起)
1) 不带 else 分支的 if 语句
2)else 分支的 if 语句
3) 不带 else 分支的 consteval if 语句
4)else 分支的 consteval if 语句
属性 - (C++11 起) 任意数量的属性
constexpr - (C++17 起) 出现时,该语句是 constexpr if 语句
初始化语句 - (C++17 起) 下列之一:
(C++23 起)

注意,任何初始化语句 都应以分号 ; 结束。这是它常被非正式地描述成后随分号的表达式或声明的原因。

条件 - 条件
true分支语句 - 任意当条件 求值为 true 时会执行的语句
false分支语句 - 条件 求值为 false 时会执行的语句
复合语句 - 当此 if 语句在明显常量求值语境下求值时会执行的复合语句consteval 前附 ! 的情况下条件变成“不在这种语境下求值”)
语句 - 当此 if 语句不在明显常量求值语境下求值时会执行的语句(consteval 未前附 ! 的情况下条件变成“在这种语境下求值”)

[编辑]条件

条件 可以是表达式简单声明

  • 如果在语法上它可以解析为结构化绑定声明,那么它就会作为结构化绑定声明处理。
(C++26 起)
  • 如果在语法上它既可以解析为表达式,则作为表达式处理,否则作为结构化绑定声明以外的(C++26 起)声明处理。

当控制抵达条件 时,条件会产生一个值,该值用于确定控制会继续前往哪个分支。

[编辑]表达式

如果条件 是表达式,那么它产生的值就是该表达式按语境转换到 bool 的值。如果该转换非良构,那么程序也非良构。

[编辑]声明

如果条件 是简单声明,那么它产生的值是决定变量(见下文)按语境转换到 bool 的值。如果该转换非良构,那么程序也非良构。

[编辑]非结构化绑定声明

声明具有以下限制:

  • 语法符合以下形式:
  • 类型说明符序列声明符=赋值表达式
(C++11 前)
  • 属性说明符序列(可选)声明说明符序列声明符花括号或等号初始化器
(C++11 起)

这种声明的决定变量就是它声明的变量。

结构化绑定声明

声明具有以下限制:

这种声明的决定变量是由声明引入的虚设变量 e

(C++26 起)

[编辑]分支选择

如果条件 产生了 true,那么就会执行true分支语句

如果 if 语句存在 else 部分,且条件 产生了 false,那么就会执行 false分支语句

如果 if 语句存在 else 部分,且 true分支语句 也是 if 语句,那么内层 if 语句必须也含有 else 部分(换言之,在嵌套 if 语句中 else 会关联到最近的尚未有关联 elseif)。

#include <iostream>   int main(){// 带 else 子句的简单 if 语句int i =2;if(i >2)std::cout<< i <<" 大于 2\n";elsestd::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 语句等价于

{
初始化语句
属性 (可选)ifconstexpr(可选)(条件)
true分支语句

}

{
初始化语句
属性 (可选)ifconstexpr(可选)(条件)
true分支语句
else
false分支语句

}

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

std::map<int, std::string> m;std::mutex mx;externbool 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(constauto keywords ={"if", "for", "while"}; std::ranges::any_of(keywords, [&tok](constchar* kw){return tok == kw;})){std::cerr<<"Token 不能是关键词\n");}}
(C++17 起)


constexpr if

ifconstexpr 开始的语句被称为 constexpr if 语句。constexpr if 语句的所有子语句都是有控制流限制的语句

在 constexpr if 语句中,条件 的值必须是可按语境转换到 bool 类型的经转换常量表达式(C++23 前)按语境转换到 bool 的表达式,其中转换为常量表达式(C++23 起)

如果条件 的值是 true,那么舍弃 false分支语句(如果存在),否则舍弃 true分支语句

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

template<typename T>auto get_value(T t){ifconstexpr(std::is_pointer_v<T>)return*t;// 对 T = int* 推导返回类型为 intelsereturn t;// 对 T = int 推导返回类型为 int}

被舍弃语句可以 ODR 使用未定义的变量:

externint x;// 不需要 x 的定义   int f(){ifconstexpr(true)return0;elseif(x)return x;elsereturn-x;}

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

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

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

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

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

template<class T>void g(){auto lm =[=](auto p){ifconstexpr(sizeof(T)==1&& sizeof p ==1){// 此条件在 g<T> 实例化后仍为值待决的,这会影响 lambda 的隐式捕获// 在实例化 lambda 函数体后,此复合语句才可能被舍弃}};}

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

template<typename T>void f(){ifconstexpr(std::is_arithmetic_v<T>)// ...else{using invalid_array =int[-1];// 非良构:对于所有 T 都非法 static_assert(false, "必须是算术类型");// CWG2518 前非良构}}

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

template<typename>constexprbool dependent_false_v =false;   template<typename T>void f(){ifconstexpr(std::is_arithmetic_v<T>)// ...else{// CWG2518 前的变通方案 static_assert(dependent_false_v<T>, "必须是算术类型");// OK}}

可以将 typedef 声明别名声明(C++23 起)用作 constexpr if 语句的初始化语句以减少类型别名的作用域。

(C++17 起)


consteval if

if consteval 开始的语句被称为 consteval if 语句。constexpr if 语句的所有子语句都是有控制流限制的语句

语句 必须是复合语句,并且在它不是复合语句时也会被视为此 consteval if 语句的一部分(因此产生编译错误):

constexprvoid f(bool b){if(true)if consteval {}else;// 错误:不是复合语句,else 不与外层 if 关联}

如果在明显常量求值语境中求值 consteval if 语句,那么就会执行复合语句。否则会执行语句(如果存在)。

如果语句以 if!consteval 开始,那么复合语句 和语句(如果存在)必须都是复合语句。这种语句不被认为是 consteval if 语句,但它等价于 consteval if 语句:

  • if!consteval {/* 语句 */} 等价于
if consteval {}else{/* 语句 */}
  • if!consteval {/* 语句1 */}else{/* 语句2 */} 等价于
if consteval {/* 语句2 */}else{/* 语句1 */}

consteval if 语句中的复合语句(或否定形式中的语句)是立即函数语境,在其中对立即函数的调用不需要是常量表达式。

#include <cmath>#include <cstdint>#include <cstring>#include <iostream>   constexprbool is_constant_evaluated()noexcept{if consteval {returntrue;}else{returnfalse;}}   constexprbool is_runtime_evaluated()noexcept{if not consteval {returntrue;}else{returnfalse;}}   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;}   constexprstd::uint64_t ipow(std::uint64_t base, std::uint8_t exp){if consteval // 使用编译时友好的算法{return ipow_ct(base, exp);}else// 使用运行时求值{returnstd::pow(base, exp);}}   int main(int, constchar* argv[]){ static_assert(ipow(0, 10)==0&& ipow(2, 10)==1024);std::cout<< ipow(std::strlen(argv[0]), 3)<<'\n';}
(C++23 起)

[编辑]注解

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

if(x)int i;// i 不再在作用域中

与下面相同

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

如果条件 是声明,那么它引入的名字的作用域是两个语句体的合并作用域:

if(int x = f()){int x;// 错误:重复声明了 x}else{int x;// 错误:重复声明了 x}

如果通过 gotolongjmp 进入 true分支语句,那么不会对条件 求值,也不会执行 false分支语句

constexpr if 语句的条件 中不允许除了到 bool 的非窄化整数转换之外的内建转换。

(C++17 起)
(C++23 前)
功能特性测试宏 标准功能特性
__cpp_if_constexpr201606L(C++17)constexprif
__cpp_if_consteval202106L(C++23)constevalif

[编辑]关键词

if, else, constexpr, consteval

[编辑]缺陷报告

下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。

缺陷报告 应用于 出版时的行为 正确行为
CWG 631 C++98 未指明通过标签抵达第一子语句时的控制流 不对条件求值,也不执行第二子语句(与 C 的行为一致)

[编辑]参阅

检测调用是否在常量求值的语境内发生
(函数)[编辑]
if 语句C 文档
close