表达式
表达式是由运算符及其操作数组成的序列,用于指定计算。
表达式求值可能会产生结果(例如,2 + 2 的求值结果为 4),也可能会产生副作用(例如,std::printf("%d", 4) 的求值会在标准输出上打印字符 '4')。
每个 C++ 表达式都具有两个独立的属性:类型和值类别。
目录 |
[编辑] 概述
- 值类别(左值、右值、泛左值、纯右值、将亡值(自 C++11 起))按其值对表达式进行分类
- 参数和子表达式的求值顺序 指定获取中间结果的顺序
[编辑] 运算符
常用运算符 | ||||||
---|---|---|---|---|---|---|
赋值 | 递增 递减 |
算术 | 逻辑 | 比较 | 成员 访问 |
其他 |
a = b |
++a |
+a |
!a |
a == b |
a[...] |
函数调用 |
a(...) | ||||||
逗号 | ||||||
a, b | ||||||
条件 | ||||||
a ? b : c | ||||||
特殊运算符 | ||||||
static_cast 将一种类型转换为另一种相关类型 |
[编辑] 转换
- 标准转换 从一种类型到另一种类型的隐式转换
-
const_cast
转换 -
static_cast
转换 -
dynamic_cast
转换 -
reinterpret_cast
转换 - 显式强制转换 使用 C 风格强制转换表示法和函数式表示法进行转换
- 用户定义的转换 使得可以指定从用户定义的类进行转换
[编辑] 内存分配
- new 表达式 动态分配内存
- delete 表达式 动态释放内存
[编辑] 其他
[编辑] 基本表达式
任何运算符的操作数都可以是其他表达式或基本表达式(例如,在 1 + 2 * 3 中,运算符 + 的操作数是 子表达式 2 * 3 和基本表达式 1)。
基本表达式可以是以下任何一种:
-
this
- 字面量(例如 2 或 "Hello, world")
- 标识符表达式,包括
- 适当声明的非限定标识符(例如 n 或 cout),
- 适当声明的限定标识符(例如 std::string::npos),以及
- 将在 声明符 中声明的标识符
(自 C++26 起) |
(自 C++11 起) | |
(自 C++17 起) | |
(自 C++20 起) |
任何括号中的表达式也被归类为基本表达式:这保证了括号的优先级高于任何运算符。括号保留值、类型和值类别。
[编辑] 字面量
字面量是 C++ 程序的标记,表示嵌入在源代码中的常量值。
- char 或 wchar_t
|
(自 C++11 起) |
|
(自 C++20 起) |
- const char[] 或 const wchar_t[]
|
(自 C++11 起) |
|
(自 C++20 起) |
- 布尔字面量是类型为 bool 的值,即 true 和 false
(自 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 起) |
|
(自 C++11 起) |
- E 隐式调用的任何函数调用,或者
- 如果 E 是函数调用或隐式调用函数,则调用中使用的每个 默认参数 的组成表达式。
表达式 E 的子表达式是 E 的直接子表达式或 E 的直接子表达式的子表达式。注意,出现在 Lambda 表达式“函数体”中的表达式不是 Lambda 表达式的子表达式。(自 C++11 起)
完整表达式是:
|
(自 C++20 起) |
- 简单声明 的声明符或 成员初始化器,包括初始化器的组成表达式,
- 在对象(不是生命周期未延长的临时对象)的生命周期结束时生成的 析构函数 的调用,或者
- 不是另一个表达式的子表达式且不属于完整表达式一部分的表达式。
如果语言结构被定义为产生函数的隐式调用,则出于此定义的目的,该语言结构的使用被视为表达式。为了满足表达式出现的语言结构的要求而应用于表达式结果的转换也被视为完整表达式的一部分。
对于初始化器,执行实体的初始化 (包括评估聚合的默认成员初始化器)(自 C++14 起) 也被视为完整表达式的一部分。
[编辑] 可能被求值的表达式
除非满足以下条件,否则表达式会被可能求值 |
(直到 C++11) | ||
以下操作数是未求值操作数,它们不会被求值
除非满足以下条件,否则表达式会被可能求值
|
(自 C++11 起) |
可能被求值的表达式是 ODR 使用。
本节内容不完整 原因:未求值操作数的示例 |
[编辑] 丢弃值表达式
丢弃值表达式 是仅因其副作用而使用的表达式。从此类表达式计算出的值将被丢弃。此类表达式包括任何 表达式语句 的完整表达式、内置逗号运算符的左侧操作数或强制转换为 void 类型的强制转换表达式的操作数。
数组到指针和函数到指针的转换永远不会应用于丢弃值表达式计算出的值。当且仅当表达式是 volatile 限定 的泛左值并且具有以下形式之一(需要内置含义,可能带有括号)时,才会应用左值到右值的转换
- id 表达式,
- 数组下标表达式,
- 类成员访问表达式,
- 间接寻址,
- 指向成员的操作,
- 条件表达式,其中第二个和第三个操作数都是这些表达式之一,
- 逗号表达式,其中右侧操作数是这些表达式之一。
此外,如果左值是 volatile 限定的类类型,则需要 volatile 复制构造函数来初始化生成的右值临时对象。
如果表达式是非 void 纯右值(在可能发生的任何左值到右值转换之后),则会发生 临时物化。 当除了强制转换为 void 以外的表达式丢弃声明为 |
(自 C++17 起) |
表达式等价如果满足以下所有条件,则多个表达式 e1、e2、...、eN 是表达式等价 的 当且仅当 e1 和 e2 是表达式等价的(这意味着 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 文档 关于 表达式
|