Операторы присваивания
Операторы присваивания изменяют значение объекта.
| Название оператора | Синтаксис | Можно перегружать | Примеры прототипов (для classT) | |
|---|---|---|---|---|
| Внутри определения класса | Вне определения класса | |||
| простое присваивание |
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 не изменяется). Для типов классов это выполняется специальной функцией-членом, описанной в разделе оператор копирующего присваивания.
| Перемещающее присваивание заменяет содержимое объекта | (начиная с 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 должно быть изменяемым lvalue.
Объект, на который ссылается target-expr, изменяется: его значение заменяется результатом new-value. Если объект имеет целочисленный тип T, а результат new-value имеет соответствующий целочисленный тип со знаком/без знака, значение объекта заменяется значением типа T с тем же представлением значения, что и у результата new-value.
Результат встроенного простого присваивания — lvalue типа target-expr, ссылающееся на target-expr. Если target-expr является битовым полем, результат также является битовым полем.
Присваивание из выражения
Если new-value является выражением, оно неявно преобразуется к типу target-expr без квалификаторов cv. Если target-expr является битовым полем, не способным представить значение выражения, результирующее значение битового поля определяется реализацией.
Если target-expr и new-value обозначают пересекающиеся объекты, поведение не определено (если только пересечение не является точным и тип не совпадает).
| Если тип target-expr имеет квалификатор volatile, присваивание считается устаревшим, если только выражение присваивания (возможно, заключённое в скобки) не является выражением с отброшенным значением или невычисляемым операндом. | (начиная с 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 для встроенных операторов простого присваивания. Кроме того:
- Для
+=и-=тип 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); |
Для каждой пары 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++.
| Отчёт об ошибке | Применён к | Опубликованное поведение | Корректное поведение |
|---|---|---|---|
| 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 | значение, присваиваемое объекту unsignedint через lvalue типа int, было неясным | уточнено |
| P2327R1 | C++20 | операторы побитового составного присваивания для типов volatile были объявлены устаревшими, хотя полезны на некоторых платформах | они не являются устаревшими |
См. также
| Основные операторы | ||||||
|---|---|---|---|---|---|---|
| Присваивание |
Инкремент, декремент | Арифметические | Логические | Сравнения |
Доступ к членам | Другие |
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[...] *a &a a->b a.b a->*b a.*b | вызов функцииa(...) |
запятаяa, b |
||||||
условный операторa ? b : c |
||||||
| Специальные операторы | ||||||
|
|
||||||
| Документация по C для Операторы присваивания |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/operator_assignment