命名空间
变体
操作

表达式

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

表达式是**运算符**及其**操作数**的序列,它指定了计算。

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

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

目录

[编辑] 概述

  • 值类别 (左值、右值、泛左值、纯右值、将亡值(C++11 起)) 通过它们的值对表达式进行分类
  • 求值顺序 指定了参数和子表达式获得中间结果的顺序

[编辑] 运算符

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

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 起)

[编辑] 完整表达式

**组成表达式**定义如下

  • 表达式的组成表达式就是该表达式。
  • 大括号括起来的初始化列表或(可能带括号的)表达式列表的组成表达式是各自列表元素的组成表达式。
  • = 开头的初始化器的组成表达式是 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 起)
  • 如果 E 是一个lambda 表达式,则是由复制捕获的实体的初始化以及捕获的初始化器的组成表达式,
(C++11 起)
  • E 隐式调用的任何函数调用,或
  • 如果 E 是函数调用或隐式调用函数,则是在调用中使用的每个默认实参的组成表达式。

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

以下表达式是**完整表达式**

(C++20 起)
(C++26 起)
  • 不是任何其他表达式的子表达式且不以其他方式作为任何完整表达式的一部分的表达式

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

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

[编辑] 潜在求值表达式

一个表达式是**潜在求值**的,除非

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

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

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

一个表达式是**潜在求值**的,除非

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

潜在求值表达式是ODR 使用

[编辑] 丢弃值表达式

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

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

  • id-expression,
  • 数组下标表达式,
  • 类成员访问表达式,
  • 解引用,
  • 指向成员的运算,
  • 条件表达式,其中第二个和第三个操作数都是这些表达式之一,
  • 逗号表达式,其中右操作数是这些表达式之一。

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

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

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

(C++17 起)

表达式等价

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

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

e1 **表达式等价于** e2 当且仅当 e1e2 表达式等价(这意味着 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 文档 关于 表达式