表达式
表达式是**运算符**及其**操作数**的序列,它指定了计算。
表达式求值可能会产生结果(例如,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 表达式 动态释放内存
[编辑] 其他
- 常量表达式 可以在编译时求值,并用于编译时上下文(模板参数、数组大小等)
-
sizeof
-
alignof
-
typeid
- 抛出表达式 (throw-expression)
[编辑] 主表达式
任何运算符的操作数可以是其他表达式或主表达式(例如,在 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 起) |
[编辑] 完整表达式
**组成表达式**定义如下
- 表达式的组成表达式就是该表达式。
- 大括号括起来的初始化列表或(可能带括号的)表达式列表的组成表达式是各自列表元素的组成表达式。
- 以
=
开头的初始化器的组成表达式是 initializer-clause 的组成表达式。
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-expression,
- 数组下标表达式,
- 类成员访问表达式,
- 解引用,
- 指向成员的运算,
- 条件表达式,其中第二个和第三个操作数都是这些表达式之一,
- 逗号表达式,其中右操作数是这些表达式之一。
此外,如果左值是 volatile 限定的类类型,则需要 volatile 复制构造函数来初始化结果的右值临时对象。
如果表达式是(在可能发生的任何左值到右值转换之后)非 void 纯右值,则发生临时实体化。 当表达式(除转换为 void 的表达式外)丢弃声明为 |
(C++17 起) |
表达式等价多个表达式 e1、e2、...、eN 是**表达式等价**的,如果所有以下条件都满足 e1 **表达式等价于** e2 当且仅当 e1 和 e2 表达式等价(这意味着 e2 也表达式等价于 e1)。 |
(C++20 起) |
[编辑] 缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
缺陷报告 | 应用于 | 发布时的行为 | 正确的行为 |
---|---|---|---|
CWG 1054 | C++98 | 给 volatile 变量赋值可能会 由于应用于赋值结果的左值到 右值转换导致不必要的读取 |
引入丢弃值表达式 并将此情况从要求转换的 情况列表中排除 |
CWG 1343 | C++98 | 聚合初始化中析构函数调用的 排序未明确指定 |
聚合初始化中的完整表达式 被明确指定 |
CWG 1383 | C++98 | 左值到右值转换应用于 丢弃值表达式的列表也包括 重载运算符 |
只包括具有内置含义的 运算符 |
CWG 1576 | C++11 | 左值到右值转换未应用于 丢弃值 volatile 将亡值表达式 |
应用转换 在这种情况下 |
CWG 2249 | C++98 | 声明符中待声明的标识符 不是 id-expression |
它们是 |
CWG 2431 | C++11 | 绑定到引用的临时对象的析构函数 调用不是完整表达式 |
它们是 |
[编辑] 参阅
C 文档 关于 表达式
|