Assignment operators
赋值运算符修改对象的值。
| 运算符名称 | 语法 | 可重载 | 原型示例(针对 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 ) ; |
|
||||
目录 |
定义
复制赋值 将对象 a 的内容替换为 b 内容的副本( b 不会被修改)。对于类类型,该操作通过特殊的成员函数执行,具体说明参见 复制赋值运算符 。
|
移动赋值 将对象 a 的内容替换为 b 的内容,尽可能避免复制( b 可能被修改)。对于类类型,这是通过特殊成员函数执行的,具体描述见 移动赋值运算符 。 |
(自 C++11 起) |
对于非类类型,复制赋值和移动赋值无法区分,统称为 直接赋值 。
复合赋值 将对象 a 的内容替换为 a 的先前值与 b 的值之间二元运算的结果。
赋值运算符语法
赋值表达式具有以下形式
目标表达式
=
新值
|
(1) | ||||||||
| 目标表达式 运算符 新值 | (2) | ||||||||
| target-expr | - | 要被赋值的表达式 [1] |
| op | - | 运算符之一: * = , / = % = , + = - = , <<= , >>= , & = , ^ = , | = |
| new-value | - | 要赋值给目标的 表达式 [2] (C++11 前) 初始化子句 (C++11 起) |
|
如果 new-value 不是表达式,该赋值表达式将永远不会匹配重载的复合赋值运算符。 |
(since 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 限定符,则该赋值操作被弃用,除非(可能带括号的)赋值表达式是 被丢弃值表达式 或 未求值操作数 。 |
(since C++20) |
来自非表达式初始化子句的赋值new-value 仅在以下情况下允许不是表达式:
#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
)
;
|
||
内置复合赋值运算符
每个内置复合赋值表达式
target-expr
op
=
new-value
的行为与表达式
target-expr
=
target-expr
op
new-value
的行为完全相同,唯一的区别在于
target-expr
仅会被计算一次。
对于内置简单赋值运算符的 target-expr 和 new-value 要求同样适用。此外:
- 对于 + = 和 - = , target-expr 的类型必须是 算术类型 ,或指向(可能带有 cv 限定符)完全定义的 对象类型 的指针。
- 对于所有其他复合赋值运算符, target-expr 的类型必须是算术类型。
在
针对用户定义运算符的重载决议
中,对于每一对
A1
和
A2
(其中
A1
是算术类型(可选地带有volatile限定符),
A2
是提升后的算术类型),以下函数签名会参与重载决议:
|
A1
&
operator
*
=
(
A1
&
, A2
)
;
|
||
|
A1
&
operator
/
=
(
A1
&
, A2
)
;
|
||
|
A1
&
operator
+
=
(
A1
&
, A2
)
;
|
||
|
A1
&
operator
-
=
(
A1
&
, A2
)
;
|
||
标签内的C++代码均保持原样未翻译,仅对页面说明性文字进行了翻译。由于提供的网页内容中不包含需要翻译的自然语言文本,故输出内容与原文完全一致。)
对于每一对
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
)
;
|
||
标签内的C++代码均保持原样未翻译。该表格展示的是C++复合赋值运算符的函数声明,这些专业术语按规则不予翻译。
对于每个可选地带有 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 |
从非表达式初始化子句到标量值的赋值
会执行直接列表初始化 |
改为执行复制列表初始化 |
| 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 文档
关于
赋值运算符
|