Copy assignment operator
Оператор копирующего присваивания — это нешаблонная нестатическая функция-член с именем operator = , которая может быть вызвана с аргументом того же типа класса и копирует содержимое аргумента без его изменения.
Синтаксис
Для синтаксиса формального оператора присваивания копированием см. объявление функции . Приведенный ниже список синтаксисов демонстрирует только подмножество всех допустимых вариантов синтаксиса оператора присваивания копированием.
тип-возвращаемого-значения
operator=(
список-параметров
);
|
(1) | ||||||||
тип-возвращаемого-значения
operator=(
список-параметров
)
тело-функции
|
(2) | ||||||||
тип-возвращаемого-значения
operator=(
список-параметров-без-умолчаний
) = default;
|
(3) | (начиная с C++11) | |||||||
тип-возвращаемого-значения
operator=(
список-параметров
) = delete;
|
(4) | (начиная с C++11) | |||||||
тип-возвращаемого-значения
имя-класса
::
operator=(
список-параметров
)
тело-функции
|
(5) | ||||||||
тип-возвращаемого-значения
имя-класса
::
operator=(
список-параметров-без-умолчаний
) = default;
|
(6) | (начиная с C++11) | |||||||
| class-name | - |
класс, для которого объявляется оператор присваивания копированием; тип класса обозначен как
T
в описаниях ниже
|
| parameter-list | - |
список параметров
только из одного параметра типа
T
,
T&
,
const
T
&
,
volatile
T
&
или
const
volatile
T
&
|
| parameter-list-no-default | - |
список параметров
только из одного параметра типа
T
,
T&
,
const
T
&
,
volatile
T
&
или
const
volatile
T
&
без аргумента по умолчанию
|
| function-body | - | тело функции оператора присваивания копированием |
| return-type | - |
любой тип, но предпочтительно
T&
для возможности цепочки присваиваний
|
Объяснение
struct X { X& operator=(X& other); // оператор копирующего присваивания X operator=(X other); // передача по значению разрешена // X operator=(const X other); // Ошибка: неверный тип параметра }; union Y { // операторы копирующего присваивания могут иметь синтаксисы, не перечисленные выше, // при условии, что они следуют общему синтаксису объявления функций // и не нарушают ограничения, перечисленные выше auto operator=(Y& other) -> Y&; // OK: завершающий тип возврата Y& operator=(this Y& self, Y& other); // OK: явный параметр объекта // Y& operator=(Y&, int num = 1); // Ошибка: содержит другие необъектные параметры };
Оператор копирующего присваивания вызывается всякий раз, когда он выбран разрешением перегрузки , например, когда объект появляется в левой части выражения присваивания.
Неявно объявленный оператор присваивания копированием
Если для типа класса не предоставлены пользовательские операторы копирующего присваивания, компилятор всегда объявляет его как inline public член класса. Этот неявно объявленный оператор копирующего присваивания имеет форму T & T :: operator = ( const T & ) если выполняются все следующие условия:
-
каждый прямой базовый класс
BтипаTимеет оператор присваивания копией, параметры которого имеют типBили const B & или const volatile B & ; -
каждый нестатический элемент данных
MтипаTклассового типа или массива классового типа имеет оператор присваивания копией, параметры которого имеют типMили const M & или const volatile M & .
В противном случае неявно объявленный оператор присваивания копированием объявляется как T & T :: operator = ( T & ) .
В соответствии с этими правилами, неявно объявленный оператор копирующего присваивания не может связываться с volatile lvalue аргументом.
Класс может иметь несколько операторов присваивания копированием, например, как T & T :: operator = ( T & ) , так и T & T :: operator = ( T ) . Если присутствуют пользовательские операторы присваивания копированием, пользователь может принудительно сгенерировать неявно объявленный оператор присваивания с помощью ключевого слова default . (начиная с C++11)
Неявно объявленный (или defaulted при первом объявлении) оператор копирующего присваивания имеет спецификацию исключений, как описано в динамической спецификации исключений (до C++17) спецификации noexcept (начиная с C++17)
Поскольку оператор копирующего присваивания всегда объявляется для любого класса, оператор присваивания базового класса всегда скрыт. Если using-объявление используется для импорта оператора присваивания из базового класса, и его тип аргумента может совпадать с типом аргумента неявного оператора присваивания производного класса, то using-объявление также скрывается неявным объявлением.
Неявно определённый оператор присваивания копированием
Если неявно объявленный оператор копирующего присваивания не является ни удаленным, ни тривиальным, он определяется (то есть генерируется и компилируется тело функции) компилятором, если odr-used или needed for constant evaluation (since C++14) . Для типов union неявно определенный оператор копирующего присваивания копирует представление объекта (как с помощью std::memmove ). Для типов классов, не являющихся union, оператор выполняет покомпонентное копирующее присваивание прямых базовых классов и нестатических членов-данных объекта в порядке их инициализации, используя встроенное присваивание для скалярных типов, покомпонентное копирующее присваивание для массивов и оператор копирующего присваивания для классовых типов (вызываемый невиртуально).
|
Неявно определенный оператор копирующего присваивания для класса
|
(since C++14)
(until C++23) |
|
Неявно определенный оператор копирующего присваивания для класса
|
(since C++23) |
|
Генерация неявно определенного оператора копирующего присваивания устаревает, если
|
(since C++11) |
Удалённый оператор копирующего присваивания
Неявно объявленный
или явно заданный по умолчанию
(since C++11)
оператор копирующего присваивания для класса
T
является
неопределенным
(until C++11)
определенным как удаленный
(since C++11)
если выполняется любое из следующих условий:
-
Tимеет нестатический член данных константно-квалифицированного неклассового типа (или, возможно, многомерного массива такового). -
Tимеет нестатический член данных ссылочного типа. -
Tимеет потенциально конструируемый подобъект классового типаM(или, возможно, многомерного массива такового), такой что разрешение перегрузки, применённое для поиска оператора присваивания копированиемM
-
- не приводит к появлению пригодного кандидата, или
- в случае, когда подобъект является variant member , выбирает нетривиальную функцию.
|
Неявно объявленный оператор копирующего присваивания для класса
|
(since C++11) |
Тривиальный оператор копирующего присваивания
Оператор копирующего присваивания для класса
T
является тривиальным, если выполняются все следующие условия:
- он не предоставлен пользователем (то есть является неявно определённым или заданным по умолчанию);
-
Tне имеет виртуальных функций-членов; -
Tне имеет виртуальных базовых классов; -
оператор присваивания копией, выбранный для каждого прямого базового класса
T, является тривиальным; -
оператор присваивания копией, выбранный для каждого нестатического члена типа класса (или массива типа класса)
T, является тривиальным.
Тривиальный оператор копирующего присваивания создает копию представления объекта, как если бы с помощью std::memmove . Все типы данных, совместимые с языком C (POD-типы), являются тривиально копируемо присваиваемыми.
Подходящий оператор копирующего присваивания
|
Оператор копирующего присваивания является подходящим, если он либо объявлен пользователем, либо одновременно объявлен неявно и может быть определен. |
(до C++11) |
|
Оператор копирующего присваивания является подходящим, если он не удален. |
(начиная с C++11)
(до C++20) |
|
Оператор копирующего присваивания является подходящим, если удовлетворены все следующие условия:
|
(начиная с C++20) |
Тривиальность подходящих операторов копирующего присваивания определяет, является ли класс тривиально копируемым типом .
Примечания
Если предоставлены и оператор копирующего присваивания, и оператор перемещающего присваивания, разрешение перегрузки выбирает перемещающее присваивание, если аргумент является rvalue (либо prvalue , такой как безымянный временный объект, либо xvalue , такой как результат std::move ), и выбирает копирующее присваивание, если аргумент является lvalue (именованный объект или функция/оператор, возвращающие lvalue-ссылку). Если предоставлен только оператор копирующего присваивания, все категории аргументов выбирают его (при условии, что он принимает аргумент по значению или по ссылке на константу, поскольку rvalue могут связываться с константными ссылками), что делает копирующее присваивание резервным вариантом для перемещающего присваивания, когда перемещение недоступно.
Не определено, присваиваются ли подобъекты виртуальных базовых классов, доступные через более чем один путь в решетке наследования, более одного раза неявно определенным оператором копирующего присваивания (то же относится к оператору перемещающего присваивания ).
См. перегрузку оператора присваивания для получения дополнительной информации о предполагаемом поведении пользовательского копирующего оператора присваивания.
Пример
#include <algorithm> #include <iostream> #include <memory> #include <string> struct A { int n; std::string s1; A() = default; A(A const&) = default; // пользовательское копирующее присваивание (идиома copy-and-swap) A& operator=(A other) { std::cout << "copy assignment of A\n"; std::swap(n, other.n); std::swap(s1, other.s1); return *this; } }; struct B : A { std::string s2; // неявно определенное копирующее присваивание }; struct C { std::unique_ptr<int[]> data; std::size_t size; // пользовательское копирующее присваивание (не по идиоме copy-and-swap) // примечание: copy-and-swap всегда перераспределяет ресурсы C& operator=(const C& other) { if (this != &other) // не самоприсваивание { if (size != other.size) // ресурс не может быть переиспользован { data.reset(new int[other.size]); size = other.size; } std::copy(&other.data[0], &other.data[0] + size, &data[0]); } return *this; } }; int main() { A a1, a2; std::cout << "a1 = a2 calls "; a1 = a2; // пользовательское копирующее присваивание B b1, b2; b2.s1 = "foo"; b2.s2 = "bar"; std::cout << "b1 = b2 calls "; b1 = b2; // неявно определенное копирующее присваивание std::cout << "b1.s1 = " << b1.s1 << "; b1.s2 = " << b1.s2 << '\n'; }
Вывод:
a1 = a2 calls copy assignment of A b1 = b2 calls copy assignment of A b1.s1 = foo; b1.s2 = bar
Отчеты о дефектах
Следующие отчеты об изменениях в поведении, содержащие описания дефектов, были применены ретроактивно к ранее опубликованным стандартам C++.
| DR | Применяется к | Поведение в опубликованной версии | Корректное поведение |
|---|---|---|---|
| CWG 1353 | C++98 |
условия, при которых неявно объявленные операторы копирующего присваивания
являются неопределенными, не учитывали многомерные массивы |
учитывать эти типы |
| CWG 2094 | C++11 |
volatile подобъект делал операторы копирующего присваивания по умолчанию
нетривиальными ( CWG issue 496 ) |
тривиальность не затрагивается |
| CWG 2171 | C++11 | operator = ( X & ) = default был нетривиальным | сделан тривиальным |
| CWG 2180 | C++11 |
оператор копирующего присваивания по умолчанию для класса
T
не определялся как удаленный,
если
T
является абстрактным и имеет прямые виртуальные базовые классы,
не поддерживающие копирующее присваивание |
оператор определяется
как удаленный в этом случае |
| CWG 2595 | C++20 |
оператор копирующего присваивания не был доступен, если существует
другой оператор копирующего присваивания, который является более ограниченным, но не удовлетворяет своим ассоциированным ограничениям |
он может быть доступен
в этом случае |