表达式
表达式是由运算符和它们的操作数组成的序列,它指定一项计算。
表达式求值可能产生结果(例如,2 + 2 的求值产生结果 4),并且可能产生副作用(例如,std::printf("%d", 4) 的求值在标准输出上打印字符 '4')。
每个 C++ 表达式都由两个独立的属性表征:类型和值类别。
内容 |
[编辑] 通用
[编辑] 运算符
常用运算符 | ||||||
---|---|---|---|---|---|---|
赋值 | 自增 自减 |
算术 | 逻辑 | 比较 | 成员 访问 |
其他 |
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++26 起) |
- 不是任何其他表达式的子表达式,并且不是任何完整表达式的其他部分的表达式
如果语言构造被定义为产生函数的隐式调用,则为了此定义的目的,语言构造的使用被认为是表达式。为了满足表达式出现的语言构造的要求而应用于表达式结果的转换也被认为是完整表达式的一部分。
对于初始化器,执行实体的初始化(包含求值聚合体的默认成员初始化器)(C++14 起)也被认为是完整表达式的一部分。
[编辑] 潜在求值表达式
除非出现以下情况,否则表达式是潜在求值的 |
(直至 C++11) | ||
下列操作数是未求值操作数,它们不被求值
除非出现以下情况,否则表达式是潜在求值的
|
(C++11 起) |
潜在求值表达式是ODR 用途。
本节不完整 原因:未求值操作数的示例 |
[编辑] 弃值表达式
弃值表达式是仅用于其副作用的表达式。从此类表达式计算的值被丢弃。此类表达式包括任何表达式语句的完整表达式、内置逗号运算符的左侧操作数,或转型到 void 类型的转型表达式的操作数。
数组到指针和函数到指针的转换永远不应用于由弃值表达式计算的值。当且仅当表达式是volatile 限定的泛左值并具有以下形式之一时(需要内置含义,可能带圆括号),才应用左值到右值转换
- id 表达式,
- 数组下标表达式,
- 类成员访问表达式,
- 解引用,
- 指向成员运算符,
- 条件表达式,其中第二个和第三个操作数是这些表达式之一,
- 逗号表达式,其中右侧操作数是这些表达式之一。
此外,如果左值是 volatile 限定的类类型,则需要 volatile 复制构造函数来初始化生成的右值临时量。
如果表达式是非 void 纯右值(在可能已发生的任何左值到右值转换之后),则会发生临时量实质化。 当除转型到 void 之外的表达式丢弃声明为 |
(C++17 起) |
表达式等价性如果满足以下所有条件,则多个表达式 e1、e2、...、eN 是表达式等价的 当且仅当 e1 和 e2 是表达式等价的时,e1 才表达式等价于 e2(这意味着 e2 也表达式等价于 e1)。 |
(C++20 起) |
[编辑] 缺陷报告
以下行为变更缺陷报告被追溯应用于先前发布的 C++ 标准。
DR | 应用于 | 已发布行为 | 正确行为 |
---|---|---|---|
CWG 1054 | C++98 | 向 volatile 变量赋值可能 由于应用于赋值结果的左值到 右值转换而导致不必要的读取 |
引入弃值表达式 并将此情况从列表排除 需要转换的情况 |
CWG 1343 | C++98 | 聚合初始化中的析构函数调用顺序 未充分指定 |
聚合初始化中的完整表达式 得到良好指定 |
CWG 1383 | C++98 | 左值到右值转换应用于弃值表达式 的表达式列表也涵盖了重载运算符 仅涵盖运算符 |
具有内置含义的运算符 具有内置含义的运算符 |
CWG 1576 | C++11 | 左值到右值转换未应用于 弃值 volatile 将亡值表达式 |
应用转换 在这种情况下 |
CWG 2249 | C++98 | 要在声明符中声明的标识符 不是 id 表达式 |
它们是 |
CWG 2431 | C++11 | 绑定到引用的临时量的析构函数的调用 不是完整表达式 |
它们是 |
[编辑] 参见
C 文档 关于 表达式
|