Spec-Zone.ru › C++

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

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

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

Объяснение

Оператор копирующего присваивания заменяет содержимое объекта a копией содержимого b (b не изменяется). Для типов классов это специальный член-функция, описанный в операторе копирующего присваивания.

Оператор перемещающего присваивания заменяет содержимое объекта a содержимым b, избегая копирования, если это возможно (b может быть изменено). Для типов классов это специальный член-функция, описанный в операторе перемещающего присваивания.

(с C++11)

Для типов, не являющихся классами, копирующие и перемещающие присваивания неразличимы и называются прямым присваиванием.

Операторы сложного присваивания заменяют содержимое объекта a результатом бинарной операции между предыдущим значением a и значением b.

Встроенное прямое присваивание

Выражения прямого присваивания имеют вид

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

Для встроенного оператора lhs может иметь любой неконстантный скалярный тип, а rhs должен быть неявно преобразуем к типу lhs.

Оператор прямого присваивания ожидает изменяемый lvalue в качестве своего левого операнда и выражение rvalue или список инициализации(с C++11) в качестве своего правого операнда и возвращает lvalue, определяющий левый операнд после модификации. Результат является полем бита, если левый операнд является полем бита.

Для типов, не являющихся классами, правый операнд сначала неявно преобразуется к неприведенному типу левого операнда, а затем его значение копируется в объект, определяемый левым операндом.

Когда левый операнд имеет тип ссылки, оператор присваивания изменяет объект, к которому ссылается.

Если левый и правый операнды идентифицируют перекрывающиеся объекты, поведение является неопределённым (если перекрытие точное и тип тот же).

Если правый операнд является списком инициализации

  • если выражение E1 имеет скалярный тип,
    • выражение E1 = {} эквивалентно E1 = T{}, где T является типом E1.
    • выражение E1 = {E2} эквивалентно E1 = T{E2}, где T является типом E1.
  • если выражение E1 имеет тип класса, синтаксис E1 = {args...} генерирует вызов оператора присваивания со списком инициализации в качестве аргумента, который затем выбирает соответствующий оператор присваивания, следуя правилам разрешения перегрузки. Обратите внимание, что если доступен оператор присваивания нешаблонного типа из какого-либо типа, не являющегося классом, он имеет приоритет над операторами копирования/перемещения в E1 = {}, потому что {} в неклассовый тип является идентичным преобразованием, которое имеет более высокий приоритет, чем пользовательское преобразование от {} к типу класса.
(с C++11)

Использование lvalue с квалификатором volatile неклассового типа в качестве левого операнда встроенного оператора прямого присваивания устарело, если выражение присваивания появляется в контексте невычисления или является выражением с отбрасываемым значением.

(с C++20)

При разрешении перегрузки для пользовательских операторов для каждого типа 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);

Пример

#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

Встроенное сложное присваивание

Выражения сложного присваивания имеют вид

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 — изменяемое выражение lvalue, а E2 — выражение rvalue или список инициализации в фигурных скобках(с 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);

Для каждого типа объекта T с необязательными cv-квалификаторами в разрешении перегрузки участвуют следующие сигнатуры функций:

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

Пример

Отчеты об ошибках

Следующие отчеты об ошибках, изменяющие поведение, были применены ретроактивно к ранее опубликованным стандартам C++.

DR Применимо к Поведение при публикации Правильное поведение
CWG 1527 C++11 при присваивании объектам класса, правое операнд
могло быть списком инициализации только тогда,
когда присваивание определено пользователем
оператором присваивания
устранено ограничение для пользовательского
оператора присваивания
CWG 1538 C++11 E1 = {E2} эквивалентно E1 = T(E2),
(T — тип E1), это ввело преобразование стиля C
экв. E1 = T{E2}
P2327R1 C++20 битовые операторы составного присваивания для типов volatile
были устаревшими, будучи полезными для некоторых платформ
они не устарели
CWG 2654 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 documentation для Операторов присваивания

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

Spec-Zone.ru

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