Оператор копирующего присваивания
Оператор копирующего присваивания — это нешаблонная нестатическая функция-член с именем operator=, которую можно вызвать с аргументом того же типа класса и которая копирует содержимое аргумента, не изменяя сам аргумент.
Синтаксис
Формальный синтаксис оператора копирующего присваивания см. в разделе объявление функции. Приведённый ниже список синтаксических форм демонстрирует лишь часть всех допустимых синтаксических форм оператора копирующего присваивания.
return-type operator=(parameter-list ); | (1) | |
return-type operator=(parameter-list ) function-body | (2) | |
return-type operator=(parameter-list-no-default ) = default; | (3) | (начиная с C++11) |
return-type operator=(parameter-list ) = delete; | (4) | (начиная с C++11) |
return-type class-name ::operator=(parameter-list ) function-body | (5) | |
return-type class-name ::operator=(parameter-list-no-default ) = default; | (6) | (начиная с C++11) |
| class-name | - | класс, для которого объявляется оператор копирующего присваивания; в приведённых ниже описаниях тип класса обозначен как T |
| parameter-list | - | список параметров только с одним параметром, имеющим тип T, T&, constT&, volatileT& или constvolatileT& |
| parameter-list-no-default | - | список параметров только с одним параметром, имеющим тип T, T&, constT&, volatileT& или constvolatileT& и не имеющим аргумента по умолчанию |
| function-body | - | тело функции оператора копирующего присваивания |
| return-type | - | любой тип, но предпочтителен T&, чтобы можно было объединять присваивания в цепочки |
Пояснение
struct X
{
X& operator=(X& other); // copy assignment operator
X operator=(X other); // pass-by-value is allowed
// X operator=(const X other); // Error: incorrect parameter type
};
union Y
{
// copy assignment operators can have syntaxes not listed above,
// as long as they follow the general function declaration syntax
// and do not violate the restrictions listed above
auto operator=(Y& other) -> Y&; // OK: trailing return type
Y& operator=(this Y& self, Y& other); // OK: explicit object parameter
// Y& operator=(Y&, int num = 1); // Error: has other non-object parameters
};
Оператор копирующего присваивания вызывается, когда он выбран при разрешении перегрузки, например, когда объект находится слева от оператора присваивания.
Неявно объявленный оператор копирующего присваивания
Если для типа класса не предоставлены пользовательские операторы копирующего присваивания, компилятор всегда объявляет такой оператор как член класса inlinepublic. Этот неявно объявленный оператор копирующего присваивания имеет форму T& T::operator=(const T&), если выполняются все следующие условия:
- каждая прямая базовая
BтипаTимеет оператор копирующего присваивания, параметры которого имеют типB, constB& или constvolatileB&; - каждый нестатический член-данных
MтипаT, имеющий тип класса или массив типа класса, имеет оператор копирующего присваивания, параметры которого имеют типM, constM& или constvolatileM&.
В противном случае неявно объявленный оператор копирующего присваивания объявляется как T& T::operator=(T&).
Из-за этих правил неявно объявленный оператор копирующего присваивания не может связываться с аргументом-левосторонним значением типа volatile.
У класса может быть несколько операторов копирующего присваивания, например, и T& T::operator=(T&), и T& T::operator=(T). Если имеются пользовательские операторы копирующего присваивания, пользователь всё ещё может принудительно сгенерировать неявно объявленный оператор копирующего присваивания с помощью ключевого слова default.(начиная с C++11)
Неявно объявленный (или заданный как defaulted при первом объявлении) оператор копирующего присваивания имеет спецификацию исключений, описанную в разделе динамическая спецификация исключений(до C++17)спецификация noexcept(начиная с C++17)
Поскольку оператор копирующего присваивания всегда объявляется для любого класса, оператор присваивания базового класса всегда скрыт. Если для получения оператора присваивания из базового класса используется объявление using, а тип его аргумента может совпадать с типом аргумента неявного оператора присваивания производного класса, такое объявление using также скрывается неявным объявлением.
Неявно определённый оператор копирующего присваивания
Если неявно объявленный оператор копирующего присваивания не удалён и не является тривиальным, компилятор определяет его (то есть генерирует и компилирует тело функции), если он используется по правилу ODR или требуется для вычисления константного выражения(начиная с C++14). Для типов-объединений неявно определённый оператор копирующего присваивания копирует представление объекта (как если бы вызывался std::memmove). Для типов классов, не являющихся объединениями, оператор выполняет поэлементное копирующее присваивание прямых базовых классов объекта и нестатических членов-данных в порядке их инициализации, используя встроенное присваивание для скалярных типов, поэлементное копирующее присваивание для массивов и оператор копирующего присваивания для типов классов (вызываемый невиртуально).
| Неявно определённый оператор копирующего присваивания для класса
|
(начиная с C++14) (до C++23) |
| Неявно определённый оператор копирующего присваивания для класса | (начиная с C++23) |
| Генерация неявно определённого оператора копирующего присваивания считается устаревшей, если | (начиная с C++11) |
Удалённый оператор копирующего присваивания
Неявно объявленный или явно заданный как defaulted(начиная с C++11) оператор копирующего присваивания класса T является неопределённым(до C++11)определённым как удалённый(начиная с C++11), если выполняется любое из следующих условий:
-
Tсодержит нестатический член-данных константного неклассового типа (или, возможно, многомерный массив такого типа). -
Tсодержит нестатический член-данных ссылочного типа. -
Tсодержит потенциально конструируемый подобъект типа классаM(или, возможно, многомерный массив такого типа), для которого разрешение перегрузки при поиске оператора копирующего присваиванияM - не находит подходящего кандидата, или
- в случае, если подобъект является вариантным членом, выбирает нетривиальную функцию.
| Неявно объявленный оператор копирующего присваивания класса | (начиная с C++11) |
Тривиальный оператор копирующего присваивания
Оператор копирующего присваивания класса T является тривиальным, если выполняются все следующие условия:
- он не предоставлен пользователем (то есть определён неявно или задан как defaulted);
-
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;
// user-defined copy assignment (copy-and-swap idiom)
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;
// implicitly-defined copy assignment
};
struct C
{
std::unique_ptr<int[]> data;
std::size_t size;
// user-defined copy assignment (non copy-and-swap idiom)
// note: copy-and-swap would always reallocate resources
C& operator=(const C& other)
{
if (this != &other) // not a self-assignment
{
if (size != other.size) // resource cannot be reused
{
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; // user-defined copy assignment
B b1, b2;
b2.s1 = "foo";
b2.s2 = "bar";
std::cout << "b1 = b2 calls ";
b1 = b2; // implicitly-defined copy assignment
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-подобъект делал заданные как defaulted операторы копирующего присваивания нетривиальными (вопрос CWG 496) | тривиальность не затрагивается |
| CWG 2171 | C++11 |
operator=(X&) = default был нетривиальным | сделан тривиальным |
| CWG 2180 | C++11 | заданный как defaulted оператор копирующего присваивания класса T не определялся как удалённый,если T является абстрактным и имеет прямые виртуальные базовые классы, которые нельзя копировать присваиванием | в этом случае оператор определяется как удалённый |
| CWG 2595 | C++20 | оператор копирующего присваивания не считался допустимым, если существовал другой оператор копирующего присваивания, который был более ограничивающим, но его связанные ограничения не удовлетворялись | в этом случае он может считаться допустимым |
См. также
- преобразующий конструктор
- конструктор копирования
- элизия копирования
- конструктор по умолчанию
- деструктор
explicit- инициализация
- перемещающее присваивание
- конструктор перемещения
new
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/as_operator