Spec-Zone.ru › C++

Оператор копирующего присваивания

Оператор копирующего присваивания — это нешаблонная нестатическая функция-член с именем 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&, чтобы можно было объединять присваивания в цепочки

Пояснение

1) Объявление оператора копирующего присваивания внутри определения класса.
2-4) Определение оператора копирующего присваивания внутри определения класса.
3) Оператор копирующего присваивания явно задан как defaulted.
4) Оператор копирующего присваивания удалён.
5,6) Определение оператора копирующего присваивания вне определения класса (класс должен содержать объявление (1)).
6) Оператор копирующего присваивания явно задан как defaulted.
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). Для типов классов, не являющихся объединениями, оператор выполняет поэлементное копирующее присваивание прямых базовых классов объекта и нестатических членов-данных в порядке их инициализации, используя встроенное присваивание для скалярных типов, поэлементное копирующее присваивание для массивов и оператор копирующего присваивания для типов классов (вызываемый невиртуально).

Неявно определённый оператор копирующего присваивания для класса T является constexpr, если

  • T является литеральным типом, и
  • оператор присваивания, выбранный для копирования каждого подобъекта прямого базового класса, является constexpr-функцией, и
  • для каждого нестатического члена-данных T, имеющего тип класса (или массив такого типа), оператор присваивания, выбранный для копирования этого члена, является constexpr-функцией.
(начиная с C++14)
(до C++23)

Неявно определённый оператор копирующего присваивания для класса T является constexpr.

(начиная с C++23)

Генерация неявно определённого оператора копирующего присваивания считается устаревшей, если T содержит пользовательский деструктор или пользовательский конструктор копирования.

(начиная с C++11)

Удалённый оператор копирующего присваивания

Неявно объявленный или явно заданный как defaulted(начиная с C++11) оператор копирующего присваивания класса T является неопределённым(до C++11)определённым как удалённый(начиная с C++11), если выполняется любое из следующих условий:

  • T содержит нестатический член-данных константного неклассового типа (или, возможно, многомерный массив такого типа).
  • T содержит нестатический член-данных ссылочного типа.
  • T содержит потенциально конструируемый подобъект типа класса M (или, возможно, многомерный массив такого типа), для которого разрешение перегрузки при поиске оператора копирующего присваивания M
    • не находит подходящего кандидата, или
    • в случае, если подобъект является вариантным членом, выбирает нетривиальную функцию.

Неявно объявленный оператор копирующего присваивания класса T определяется как удалённый, если T объявляет конструктор перемещения или оператор перемещающего присваивания.

(начиная с 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

Spec-Zone.ru

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