Spec-Zone.ru › C++

Операторы присваивания

Операторы присваивания изменяют значение объекта.

Название оператора Синтаксис Можно перегружать Примеры прототипов (для 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);
Примечания
  • Все встроенные операторы присваивания возвращают *this, и большинство перегрузок, определённых пользователем, также возвращают *this, что позволяет использовать определённые пользователем операторы так же, как встроенные. Однако в перегрузке оператора, определённой пользователем, в качестве возвращаемого типа можно использовать любой тип (в том числе void).
  • T2 может быть любым типом, включая T.

Определения

Копирующее присваивание заменяет содержимое объекта a копией содержимого b (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), присваиваемое целевому объекту
  1. target-expr должно иметь более высокий приоритет, чем выражение присваивания.
  2. new-value не может быть выражением с запятой, поскольку его приоритет ниже.
1) Выражение простого присваивания.
2) Выражение составного присваивания.

Если 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 может не быть выражением только в следующих случаях:

  • target-expr имеет скалярный тип T, а new-value пусто или содержит только один элемент. В этом случае, если считать, что вымышленная переменная t объявлена и инициализирована как T t = new-value , смысл x = new-value  — x = t.
  • target-expr имеет тип класса. В этом случае 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
Специальные операторы

static_cast преобразует один тип в другой связанный тип
dynamic_cast выполняет преобразование в иерархиях наследования
const_cast добавляет или удаляет квалификаторы cv
reinterpret_cast преобразует тип в несвязанный тип
Приведение типа в стиле C преобразует один тип в другой посредством сочетания static_cast, const_cast и reinterpret_cast
new создаёт объекты с динамической длительностью хранения
delete уничтожает объекты, ранее созданные выражением new, и освобождает занятую ими область памяти
sizeof запрашивает размер типа
sizeof... запрашивает размер пакета (начиная с C++11)
typeid запрашивает информацию о типе
noexcept проверяет, может ли выражение выбросить исключение (начиная с C++11)
alignof запрашивает требования к выравниванию типа (начиная с C++11)
^^ создаёт значение рефлексии на основе синтаксической конструкции (начиная с C++26)

Документация по C для Операторы присваивания

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/operator_assignment

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API