命名空间
变体
操作

表达式

来自 cppreference.com
< cpp‎ | 语言
 
 
C++ 语言
 
 

表达式是由运算符及其操作数组成的序列,用于指定计算。

表达式求值可能会产生结果(例如,2 + 2 的求值结果为 4),也可能会产生副作用(例如,std::printf("%d", 4) 的求值会在标准输出上打印字符 '4')。

每个 C++ 表达式都具有两个独立的属性:类型和值类别。

目录

[编辑] 概述

[编辑] 运算符

常用运算符
赋值 递增
递减
算术 逻辑 比较 成员
访问
其他

a = b
a += b
a -= b
a *= b
a /= b
a %= b
a &= b
a |= b
a ^= b
a <<= b
a >>= b

++a
--a
a++
a--

+a
-a
a + b
a - b
a * b
a / b
a % b
~a
a & b
a | b
a ^ b
a << b
a >> b

!a
a && b
a || b

a == b
a != b
a < b
a > b
a <= b
a >= b
a <=> b

a[...]
*a
&a
a->b
a.b
a->*b
a.*b

函数调用
a(...)
逗号
a, b
条件
a ? b : c
特殊运算符

static_cast 将一种类型转换为另一种相关类型
dynamic_cast 在继承层次结构中进行转换
const_cast 添加或删除 cv 限定符
reinterpret_cast 将类型转换为不相关类型
C 风格的强制转换 通过混合使用 static_castconst_castreinterpret_cast 将一种类型转换为另一种类型
new 创建具有动态存储期限的对象
delete 销毁之前由 new 表达式创建的对象并释放获取的内存区域
sizeof 查询类型的大小
sizeof... 查询 参数包 的大小 (自 C++11 起)
typeid 查询类型的类型信息
noexcept 检查表达式是否可以抛出异常 (自 C++11 起)
alignof 查询类型的对齐要求 (自 C++11 起)

[编辑] 转换

[编辑] 内存分配

[编辑] 其他

[编辑] 基本表达式

任何运算符的操作数都可以是其他表达式或基本表达式(例如,在 1 + 2 * 3 中,运算符 + 的操作数是 子表达式 2 * 3 和基本表达式 1)。

基本表达式可以是以下任何一种:

(自 C++26 起)
(自 C++11 起)
(自 C++17 起)
(自 C++20 起)

任何括号中的表达式也被归类为基本表达式:这保证了括号的优先级高于任何运算符。括号保留值、类型和值类别。

[编辑] 字面量

字面量是 C++ 程序的标记,表示嵌入在源代码中的常量值。

  • charwchar_t
  • char16_tchar32_t
(自 C++11 起)
  • char8_t
(自 C++20 起)
  • const char[]const wchar_t[]
  • const char16_t[]const char32_t[]
(自 C++11 起)
  • const char8_t[]
(自 C++20 起)
(自 C++11 起)

[编辑] 完整表达式

组成表达式定义如下:

  • 表达式的组成表达式就是该表达式本身。
  • 花括号初始化列表或(可能带括号的)表达式列表的组成表达式是相应列表元素的组成表达式。
  • 形式为 = 初始化器子句花括号或等号初始化器 的组成表达式是 初始化器子句 的组成表达式。
int num1 = 0;
num1 += 1; // Case 1: the constituent expression of `num += 1` is `num += 1`
 
int arr2[2] = {2, 22} // Case 2: the constituent expressions
                      //         of `{2, 22}` are `2` and `22`
                      // Case 3: the constituent expressions of ` = {2, 22}`
                      //         are the constituent expressions of `{2, 22}`
                      //         (i.e. also `2` and `22`)

表达式 E直接子表达式是:

  • E 的操作数的组成表达式,
(自 C++14 起)
  • 如果 ELambda 表达式,则按值捕获的实体的初始化和捕获的初始化器的组成表达式,
(自 C++11 起)
  • E 隐式调用的任何函数调用,或者
  • 如果 E 是函数调用或隐式调用函数,则调用中使用的每个 默认参数 的组成表达式。

表达式 E子表达式E 的直接子表达式或 E 的直接子表达式的子表达式。注意,出现在 Lambda 表达式“函数体”中的表达式不是 Lambda 表达式的子表达式。(自 C++11 起)

完整表达式是:

(自 C++20 起)
  • 简单声明 的声明符或 成员初始化器,包括初始化器的组成表达式,
  • 在对象(不是生命周期未延长的临时对象)的生命周期结束时生成的 析构函数 的调用,或者
  • 不是另一个表达式的子表达式且不属于完整表达式一部分的表达式。

如果语言结构被定义为产生函数的隐式调用,则出于此定义的目的,该语言结构的使用被视为表达式。为了满足表达式出现的语言结构的要求而应用于表达式结果的转换也被视为完整表达式的一部分。

对于初始化器,执行实体的初始化 (包括评估聚合的默认成员初始化器)(自 C++14 起) 也被视为完整表达式的一部分。

[编辑] 可能被求值的表达式

除非满足以下条件,否则表达式会被可能求值

  • 它是 sizeof 运算符的操作数,或者
  • 它是 typeid 运算符的操作数,并且不指定 多态 类类型的左值。
(直到 C++11)

以下操作数是未求值操作数,它们不会被求值

  • typeid 运算符应用到的表达式,除了多态类类型的泛左值
  • sizeof 运算符的操作数表达式
  • noexcept 运算符的操作数
  • decltype 说明符的操作数
(自 C++20 起)

除非满足以下条件,否则表达式会被可能求值

  • 它是一个未求值操作数,或者
  • 它是未求值操作数的子表达式。
(自 C++11 起)

可能被求值的表达式是 ODR 使用

[编辑] 丢弃值表达式

丢弃值表达式 是仅因其副作用而使用的表达式。从此类表达式计算出的值将被丢弃。此类表达式包括任何 表达式语句 的完整表达式、内置逗号运算符的左侧操作数或强制转换为 void 类型的强制转换表达式的操作数。

数组到指针和函数到指针的转换永远不会应用于丢弃值表达式计算出的值。当且仅当表达式是 volatile 限定 的泛左值并且具有以下形式之一(需要内置含义,可能带有括号)时,才会应用左值到右值的转换

  • id 表达式,
  • 数组下标表达式,
  • 类成员访问表达式,
  • 间接寻址,
  • 指向成员的操作,
  • 条件表达式,其中第二个和第三个操作数都是这些表达式之一,
  • 逗号表达式,其中右侧操作数是这些表达式之一。

此外,如果左值是 volatile 限定的类类型,则需要 volatile 复制构造函数来初始化生成的右值临时对象。

如果表达式是非 void 纯右值(在可能发生的任何左值到右值转换之后),则会发生 临时物化

当除了强制转换为 void 以外的表达式丢弃声明为 [[nodiscard]] 的值时,编译器可能会发出警告。

(自 C++17 起)

表达式等价

如果满足以下所有条件,则多个表达式 e1e2、...、eN表达式等价

  1. 它们具有相同的效果。
  2. 它们要么都是 常量子表达式,要么都不是。
  3. 它们要么都是 noexcept,要么都不是。

当且仅当 e1e2 是表达式等价的(这意味着 e2 也与 e1 表达式等价),e1表达式等价于 e2

(自 C++20 起)

[编辑] 缺陷报告

以下更改行为的缺陷报告已追溯应用于先前发布的 C++ 标准。

DR 应用于 发布时的行为 正确行为
CWG 1054 C++98 由于应用于赋值结果的左值到右值转换,
将值赋给 volatile 变量可能会导致不必要的读取
引入丢弃值表达式
并将此情况从需要转换的情况列表中排除
CWG 1343 C++98 聚合初始化中析构函数调用
的顺序未明确规定
聚合初始化中的完整表达式
已明确规定
CWG 1383 C++98 将左值到右值转换应用于
丢弃值表达式的表达式列表还涵盖了重载运算符
仅涵盖具有内置含义的运算符
CWG 1576 C++11 左值到右值转换未应用于
丢弃值的 volatile xvalue 表达式
在这种情况下应用转换
CWG 2249 C++98 要在声明符中声明的标识符
不是 id 表达式
它们是
CWG 2431 C++11 绑定到引用的临时对象的析构函数调用
不是完整表达式
它们是

[编辑] 参见

C 文档 关于 表达式