赋值运算符
赋值运算符修改对象的值。
运算符名 | 语法 | 可重载 | 原型示例(对于 class T) | |
---|---|---|---|---|
类内定义 | 类外定义 | |||
简单赋值 | a = b
|
是 | T& T::operator =(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
|
是 | T& T::operator >>=(const T2& b); | T& operator >>=(T& a, const T2& b); |
|
定义
复制赋值 以 b 内容的副本替换对象 a 的内容(不修改 b)。对于类类型,这会在一种特殊成员函数中进行,描述见复制赋值运算符。
移动赋值 以 b 的内容替换对象 a 的内容,并尽可能避免复制(可以修改 b)。对于类类型,这会在一种特殊成员函数中进行,描述见移动赋值运算符。 |
(C++11 起) |
对于非类类型,对复制与移动赋值不加以区分,它们都被称作直接赋值。
复合赋值 以 a 的值和 b 的值间的二元运算结果替换对象 a 的内容。
赋值运算符语法
赋值表达式的形式为
目标表达式 = 新值
|
(1) | ||||||||
目标表达式 运算符 新值 | (2) | ||||||||
目标表达式 | - | 要被赋值的表达式[1] |
运算符 | - | *=、/=、%=、+=、-=、<<=、>>=、&=、^=、|= 之一 |
新值 | - | 要赋值给目标的表达式[2] (C++11 前)初始化器子句 (C++11 起) |
如果新值 不是表达式,那么赋值表达式不会匹配重载的复合赋值运算符。 |
(C++11 起) |
内建的简单赋值运算符
对于内建的简单赋值,目标表达式 指代的对象会通过以新值 的结果进行覆盖而被修改。目标表达式 必须是可修改的左值。
内建的简单赋值是类型与目标表达式 相同,并且指代目标表达式 的左值。如果目标表达式 是位域,那么结果也是位域。
从表达式赋值
如果新值 是表达式,那么它会隐式转换到目标表达式 具有的类型的无 cv 限定版本。在目标表达式 位域无法表示该表达式的值的情况下,位域结果值由实现定义。
如果左右操作数标识的对象之间有重叠,那么行为未定义(除非二者严格重叠且类型相同)。
如果目标表达式 的类型具有 volatile 限定,那么该赋值被弃用,除非该(可被圆括号包围的)赋值表达式是弃值表达式或不求值操作数。 |
(C++20 起) |
从非表达式初始化器子句赋值只有在以下情况下,新值 才可以不是表达式:
#include <complex> std::complex<double> z; z = {1, 2}; // 表示 z.operator=({1, 2}) z += {1, 2}; // 表示 z.operator+=({1, 2}) int a, b; a = b = {1}; // 表示 a = b = 1; a = {1} = b; // 语法错误 |
(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); |
||
内建的复合赋值运算符
每个内建复合赋值运算符表达式 目标表达式
运算符 =
新值 的行为和表达式 目标表达式
=
目标表达式
运算符
新值 完全一致,除了目标表达式 只会求值一次。
内建的简单赋值运算符对目标表达式 和新值 的要求依然使用。另外:
在针对用户定义运算符的重载决议中,对每对 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; // 不是赋值 n = 1; // 直接赋值 std::cout << n << ' '; n = {}; // 零初始化,然后赋值 std::cout << n << ' '; n = 'a'; // 整型提升,然后赋值 std::cout << n << ' '; n = {'b'}; // 显式转型,然后赋值 std::cout << n << ' '; n = 1.0; // 浮点转换,然后赋值 std::cout << n << ' '; // n = {1.0}; // 编译错误(窄化转换) int& r = n; // 不是赋值 r = 2; // 通过引用赋值 std::cout << n << ' '; int* p; p = &n; // 直接赋值 p = nullptr; // 空指针转换,然后赋值 std::cout << p << ' '; struct { int a; std::string s; } obj; obj = {1, "abc"}; // 从花括号初始化器列表赋值 std::cout << obj.a << ':' << obj.s << '\n'; }
可能的输出:
1 0 97 98 1 2 (nil) 1:abc
缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
缺陷报告 | 应用于 | 出版时的行为 | 正确行为 |
---|---|---|---|
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 | 从非表达式初始化器子句对标量值进行赋值会进行直接列表初始化 | 改为进行复制列表初始化 |
P2327R1 | C++20 | volatile 类型的逐位复合赋值运算符对一些平台有用但被弃用 | 不弃用它们 |
参阅
常见运算符 | ||||||
---|---|---|---|---|---|---|
赋值 | 自增/自减 | 算术 | 逻辑 | 比较 | 成员访问 | 其他 |
a = b |
++a |
+a |
!a |
a == b |
a[b] |
函数调用 |
a(...) | ||||||
逗号 | ||||||
a, b | ||||||
条件 | ||||||
a ? b : c | ||||||
特殊运算符 | ||||||
static_cast 转换一个类型为另一相关类型 |