赋值运算符
赋值运算符修改对象的值。
运算符名称 | 语法 | 可重载 | 原型示例 (对于 class T) | |
---|---|---|---|---|
类定义内部 | 类定义外部 | |||
简单赋值 | a = b
|
是 | T& T::operator =(const T2& b); | N/A |
加法赋值 | a += b
|
是 | T& T::operator +=(const T2& b); | T& operator +=(T& a, const T2& b); |
减法赋值 | a -= b
|
是 | T& T::operator -=(const T2& b); | T& operator -=(T& a, const T2& b); |
乘法赋值 | a *= b
|
是 | T& T::operator *=(const T2& b); | T& operator *=(T& a, const T2& b); |
除法赋值 | a /= b
|
是 | T& T::operator /=(const T2& b); | T& operator /=(T& a, const T2& b); |
求余赋值 | a %= b
|
是 | T& T::operator %=(const T2& b); | T& operator %=(T& a, const T2& b); |
按位与赋值 | a &= b
|
是 | T& T::operator &=(const T2& b); | T& operator &=(T& a, const T2& b); |
按位或赋值 | a |= b
|
是 | T& T::operator |=(const T2& b); | T& operator |=(T& a, const T2& b); |
按位异或赋值 | a ^= b
|
是 | T& T::operator ^=(const T2& b); | T& operator ^=(T& a, const T2& b); |
按位左移赋值 | a <<= b
|
是 | T& T::operator <<=(const T2& b); | T& operator <<=(T& a, const T2& b); |
按位右移赋值 | a >>= b
|
是 | T& T::operator >>=(const T2& b); | T& operator >>=(T& a, const T2& b); |
|
目录 |
[编辑] 定义
复制赋值 将对象 a 的内容替换为 b 内容的副本(b 不会被修改)。对于类类型,这是在一个特殊的成员函数中执行的,在 复制赋值运算符 中描述。
移动赋值 将对象 a 的内容替换为 b 的内容,如果可能则避免复制(b 可能会被修改)。对于类类型,这是在一个特殊的成员函数中执行的,在 移动赋值运算符 中描述。 |
(自 C++11 起) |
对于非类类型,复制和移动赋值是无法区分的,并且被称为直接赋值。
复合赋值 将对象 a 的内容替换为 a 的先前值与 b 的值之间的二元运算结果。
[编辑] 赋值运算符语法
赋值表达式具有以下形式
target-expr = new-value |
(1) | ||||||||
target-expr op new-value | (2) | ||||||||
target-expr | - | 要赋值的表达式[1] |
op | - | 以下之一:*=, /= %=, += -=, <<=, >>=, &=, ^=, |= |
new-value | - | 要赋值给目标的表达式[2](直到 C++11)初始化器子句(自 C++11 起) |
如果 new-value 不是表达式,则赋值表达式将永远不会匹配重载的复合赋值运算符。 |
(自 C++11 起) |
[编辑] 内置简单赋值运算符
对于内置简单赋值,target-expr 必须是可修改的左值。
由 target-expr 引用的对象通过将其值替换为 new-value 的结果来修改。如果引用的对象是整数类型 T
,并且 new-value 的结果是相应的有符号/无符号整数类型,则对象的值将替换为类型 T
的值,该值具有与 new-value 结果相同的值表示。
内置简单赋值的结果是 target-expr 类型的左值,引用 target-expr。如果 target-expr 是位域,则结果也是位域。
[编辑] 从表达式赋值
如果 new-value 是表达式,则它会隐式转换为 target-expr 的 cv-无限定类型。当 target-expr 是一个不能表示表达式值的位域时,位域的结果值是实现定义的。
如果 target-expr 和 new-value 标识重叠的对象,则行为是未定义的(除非重叠是精确的且类型相同)。
如果 target-expr 的类型是 volatile 限定的,则赋值已弃用,除非(可能带括号的)赋值表达式是discarded-value 表达式或未求值操作数。 |
(自 C++20 起) |
从非表达式初始化器子句赋值仅在以下情况下,new-value 才允许不是表达式
#include <complex> std::complex<double> z; z = {1, 2}; // meaning z.operator=({1, 2}) z += {1, 2}; // meaning z.operator+=({1, 2}) int a, b; a = b = {1}; // meaning a = b = 1; a = {1} = b; // syntax error |
(自 C++11 起) |
在针对用户定义的运算符的重载决议中,对于每种类型 T
,以下函数签名参与重载决议
T*& operator=(T*&, T*); |
||
T*volatile & operator=(T*volatile &, T*); |
||
对于每种枚举或指向成员类型的指针 T
,可选地 volatile 限定,以下函数签名参与重载决议
T& operator=(T&, T); |
||
对于每对 A1
和 A2
,其中 A1
是算术类型(可选 volatile 限定),A2
是提升的算术类型,以下函数签名参与重载决议
A1& operator=(A1&, A2); |
||
[编辑] 内置复合赋值运算符
每个内置复合赋值表达式 target-expr
op
=
new-value 的行为与表达式 target-expr
=
target-expr
op
new-value 的行为完全相同,除了 target-expr 只被求值一次。
内置简单赋值运算符对 target-expr 和 new-value 的要求也适用。此外
在针对用户定义的运算符的重载决议中,对于每对 A1
和 A2
,其中 A1
是算术类型(可选 volatile 限定),A2
是提升的算术类型,以下函数签名参与重载决议
A1& operator*=(A1&, A2); |
||
A1& operator/=(A1&, A2); |
||
A1& operator+=(A1&, A2); |
||
A1& operator-=(A1&, A2); |
||
对于每对 I1
和 I2
,其中 I1
是整型类型(可选 volatile 限定),I2
是提升的整型类型,以下函数签名参与重载决议
I1& operator%=(I1&, I2); |
||
I1& operator<<=(I1&, I2); |
||
I1& operator>>=(I1&, I2); |
||
I1& operator&=(I1&, I2); |
||
I1& operator^=(I1&, I2); |
||
I1& operator|=(I1&, I2); |
||
对于每个可选 cv 限定的对象类型 T
,以下函数签名参与重载决议
T*& operator+=(T*&, std::ptrdiff_t); |
||
T*& operator-=(T*&, std::ptrdiff_t); |
||
T*volatile & operator+=(T*volatile &, std::ptrdiff_t); |
||
T*volatile & operator-=(T*volatile &, std::ptrdiff_t); |
||
[编辑] 示例
#include <iostream> int main() { int n = 0; // not an assignment n = 1; // direct assignment std::cout << n << ' '; n = {}; // zero-initialization, then assignment std::cout << n << ' '; n = 'a'; // integral promotion, then assignment std::cout << n << ' '; n = {'b'}; // explicit cast, then assignment std::cout << n << ' '; n = 1.0; // floating-point conversion, then assignment std::cout << n << ' '; // n = {1.0}; // compiler error (narrowing conversion) int& r = n; // not an assignment r = 2; // assignment through reference std::cout << n << ' '; int* p; p = &n; // direct assignment p = nullptr; // null-pointer conversion, then assignment std::cout << p << ' '; struct { int a; std::string s; } obj; obj = {1, "abc"}; // assignment from a braced-init-list std::cout << obj.a << ':' << obj.s << '\n'; }
可能的输出
1 0 97 98 1 2 (nil) 1:abc
[编辑] 缺陷报告
以下行为变更缺陷报告已追溯应用于先前发布的 C++ 标准。
DR | 应用于 | 已发布行为 | 正确行为 |
---|---|---|---|
CWG 1527 | C++11 | 对于类类型对象的赋值,右操作数 只有当赋值由用户定义的 赋值运算符定义时,才能是初始化器列表 |
移除用户定义的 赋值约束 |
CWG 1538 | C++11 | E1 = {E2} 等价于 E1 = T(E2) ( T 是 E1 的类型),这引入了一个 C 风格的强制类型转换 |
它等价于 于 E1 = T{E2} |
CWG 2654 | C++20 | volatile 限定类型的复合赋值运算符 -不一致地被弃用 |
它们都不是 被弃用 |
CWG 2768 | C++11 | 从非表达式初始化器子句赋值 给标量值会执行直接列表初始化 |
执行复制列表 初始化替代 |
CWG 2901 | C++98 | 通过 int 左值赋给 unsigned int 对象的值不明确 |
已明确 |
P2327R1 | C++20 | volatile 类型的按位复合赋值运算符 在某些平台上被弃用,但仍然有用 |
它们不被 弃用 |
[编辑] 参见
常用运算符 | ||||||
---|---|---|---|---|---|---|
赋值 | 自增 自减 |
算术 | 逻辑 | 比较 | 成员 访问 |
其他 |
a = b |
++a |
+a |
!a |
a == b |
a[...] |
函数调用 a(...) |
逗号 a, b | ||||||
条件 a ? b : c | ||||||
特殊运算符 | ||||||
static_cast 将一种类型转换为另一种相关类型 |
C 语言文档 关于 赋值运算符
|