赋值运算符

来自cppreference.com
< cpp‎ | language

赋值运算符修改对象的值。

运算符名 语法 可重载 原型示例(对于 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);
注意
  • 所有内建赋值运算符都返回 *this ,而大多数用户定义重载亦返回 *this ,从而能以与内建版本相同的方式使用用户定义运算符。然而,用户定义运算符重载中,能以任何类型为返回类型(包括 void )。
  • T2 能为任何类型,包含 T

目录

[编辑] 解释

复制赋值运算符b 内容的副本(不修改 b )替换对象 a 的内容。对于类类型,这是特殊成员函数,描述于复制赋值运算符

移动赋值运算符b 的内容替换对象 a 的内容,并尽可能避免复制(可以修改 b )。对于类类型,这是特殊成员函数,描述于移动赋值运算符(C++11 起)

对于非类类型,复制与移动赋值不可辨别,而被称作直接赋值

移动赋值运算符以 a 的值和 b 的值间的二元运算结果替换对象 a 的内容。

[编辑] 内建的直接赋值

直接赋值表达式拥有形式

lhs = rhs (1)
lhs = {} (2) (C++11 起)
lhs = {rhs} (3) (C++11 起)

对于内建运算符, lhs 可拥有任何非 const 标量类型而 rhs 必须可隐式转换为 lhs 的类型。

直接赋值运算符期待可修改左值为其左运算数,右值表达式花括号初始化器列表 (C++11 起)为其右运算数,并返回标识修改后的左运算数的左值。

对于非类类型,首先隐式转换右运算数为左运算数的无 cv 限定类型,然后复制其值到左运算数所标识的对象中。

左运算数拥有引用类型时,赋值运算符修改被引用的对象。

若左右运算数标识重叠的对象,则行为未定义(除非重叠准确且类型相同)。

若右运算数为花括号初始化器列表

  • 若表达式 E1 拥有标量类型,
  • 则表达式 E1 = {} 等价于 E1 = T{} ,其中 TE1 的类型。
  • 表达式 E1 = {E2} 等价于 E1 = T{E2} ,其中 TE1 的类型。
  • 若表达式 E1 拥有类类型,则语法 E1 = {args...} 生成对以花括号初始化器列表为参数的赋值运算符的调用,然后遵循重载决议的规则选择适合的赋值运算符。注意若以某非类类型为参数的非模板赋值运算符可用,则它胜过 E1 = {} 中的移动赋值,因为从 {} 到非类类型是恒等转换,它优先于从 {} 到类类型的用户定义转换。
(C++11 起)

针对用户定义运算符的重载决议中,对于每个类型 T ,下列函数签名参与重载决议:

T*& operator=(T*&, T*);
T*volatile & operator=(T*volatile &, T*);

对于每个枚举或指向成员指针类型,可选地有 volatile 限定的 T ,下列函数签名参与重载决议:

T& operator=(T&, T );

对于每对 A1 和 A2 ,其中 A1 是算术类型(可选地有 volatile 限定)而 A2 是提升后的算术类型,下列函数签名参与重载决议:

A1& operator=(A1&, A2);

[编辑] 示例

#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;  // 不是赋值
    int* p;
 
    r = 2;       // 通过引用赋值
    std::cout << n << '\n';
    p = &n;      // 直接赋值
    p = nullptr; // 空指针转换,然后赋值
 
    struct {int a; std::string s;} obj;
    obj = {1, "abc"}; // 从花括号初始化器列表赋值
    std::cout << obj.a << ':' << obj.s << '\n';
}

输出:

1 0 97 98 1 2
1:abc

[编辑] 内建的复合赋值

复合赋值表达式拥有形式

lhs op rhs (1)
lhs op {} (2) (C++11 起)
lhs op {rhs} (3) (C++11 起)
op - *=/=%=+=-=<<=>>=&=^=|= 之一
lhs - 对于内建运算符, lhs 必须拥有任何算术类型,除非 op+=-= ,它们亦接受与 + 和 - 有相同限制的指针类型
rhs - 对于内建运算符, rhs 必须可隐式转换为 lhs

每个内建复合赋值运算符表达式 E1 op= E2 (其中 E1 是可修改左值表达式,而 E2 是右值表达式花括号初始化器列表 (C++11 起))的行为准确地与表达式 E1 = E1 op E2 的行为相同,除了只求值表达式 E1 一次,而它表现为相对于函数调用非确定顺序的单个操作(例如 f(a += b, g()) 中, += 要么完全未开始,要么从 g() 内来看已完成)。

针对用户定义运算符的重载决议对每对 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);

[编辑] 示例

[编辑] 参阅

运算符优先级

运算符重载

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

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[b]
*a
&a
a->b
a.b
a->*b
a.*b

a(...)
a, b
? :

特殊运算符

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

赋值运算符C 文档