赋值运算符

来自cppreference.com
< cpp‎ | language
 
 
 
表达式
概述
值类别(左值 lvalue 、右值 rvalue 、亡值 xvalue )
求值顺序(序列点)
常量表达式
不求值表达式
初等表达式
lambda 表达式(C++11)
字面量
整数字面量
浮点字面量
布尔字面量
字符字面量,包含转义序列
字符串字面量
空指针字面量(C++11)
用户定义字面量(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||b, a&&b, !a
比较运算符a==b, a!=b, a<b, a>b, a<=b, a>=b, a<=>b(C++20)
成员访问运算符a[b], *a, &a, a->b, a.b, a->*b, a.*b
其他运算符a(...), a,b, a?b:c
运算符的替代表示
优先级和结合性
折叠表达式(C++17)
new 表达式
delete 表达式
throw 表达式
alignof
sizeof
sizeof...(C++11)
typeid
noexcept(C++11)
运算符重载
类型转换
隐式转换
const_cast
static_cast
reinterpret_cast
dynamic_cast
显式转换 (T)a, T(a)
用户定义转换
 

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

运算符名 语法 可重载 原型示例(对于 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 在内的任何类型。

目录

[编辑] 解释

复制赋值运算符将对象 a 的内容替换为 b 的内容的一个副本(且不改动 b)。对于类类型来说,这是一种特殊成员函数,在复制赋值运算符中描述。

移动赋值运算符将对象 a 的内容替换为 b 的内容,尽可能避免进行复制(且可能改动 b。对于类类型来说,这是一种特殊成员函数,在移动赋值运算符中描述。 (C++11 起)

对于非类类型来说,复制和赋值是不可区分的,并被称为直接赋值

复合赋值运算符将对象 a 的内容替换为对 a 的前值和 b 的值实施一种二元运算后的结果。

[编辑] 内建的直接赋值

对于每个类型 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);

对于任何标量类型 T 的表达式 E1,还允许以下额外的内建赋值表达式形式:

E1 = {}
(C++11 起)
E1 = {E2}
(C++11 起)

注:以上包括所有非类类型,但不包括引用类型,数组类型,函数类型,以及 void 类型,它们是不可直接赋值的。

直接赋值运算符期望一个可改动的左值作为其做操作数,和一个右值表达式或一个花括号初始化列表(braced-init-list) (C++11 起)作为其右操作数,并返回标识了修改后的左操作数的左值。

对于非类类型来说,其右操作数首先被隐式转换为左操作数类型的 cv 未限定版本,然后将其值复制到由左操作数所标识的对象之中。

当左操作数具有引用类型时,赋值运算符将改动其所指代的对象。

如果左右操作数标识了互相有重叠的对象,则除非是精确重叠且类型相同,否则其行为未定义。

当右操作数为花括号初始化列表(braced-init-list)时,

  • 若表达式 E1 具有标量类型,则
  • 表达式 E1 = {} 等价于 E1 = T{},其中 TE1 的类型。
  • 表达式 E1 = {E2} 等价于 E1 = T{E2},其中 TE1 的类型。
  • 若表达式 E1 具有类类型,则语法 E1 = {args...} 产生对其赋值运算符的一次调用,并以该花括号初始化列表为其实参,这将根据重载解析的规则来选择适当的赋值运算符。要注意的是,如果某个非类类型的非模板赋值运算符可用,它将优先于 E1 = {} 中的复制/移动赋值,这是因为从 {} 到非类类型是一种同一转换,地位高于从 {} 到类类型的用户定义转换。
(C++11 起)

[编辑] 示例

#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 << ' ';
    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

[编辑] 内建的复合赋值

对于每对 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);

每个内建的复合赋值表达式 E1 op= E2(其中 E1 为可改动左值表达式而 E2 为右值表达式花括号初始化列表(braced-init-list) (C++11 起)),其行为与表达式 E1 = E1 op E2 的行为完全相同,但表达式 E1 仅会被求值一次,并且对于顺序不确定的函数调用来说,它表现为一次单独的运算(例如,在 f(a += b, g()) 中,g() 之内所见的 += 要么根本尚未开始要么已经完成)。

[编辑] 示例

[编辑] 另请参阅

运算符优先级

运算符重载

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

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