Пространства имён
Варианты
Действия

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

Материал из cppreference.com
< cpp‎ | language
 
 
 
 

Оператор присваивания копирования это нешаблонная нестатическая функция-элемент с именем operator=, которую можно вызвать с аргументом того же классового типа и которая копирует содержимое аргумента, не изменяя аргумент.

Содержание

[править] Синтаксис

Формальный синтаксис оператора присваивания копированием смотрите в разделе объявление функции. Список синтаксисов ниже демонстрирует толь��о подмножество всех допустимых синтаксисов операторов копирования.

возвращаемый-тип operator=(список-параметров ); (1)
возвращаемый-тип operator=(список-параметров ) тело-функции (2)
возвращаемый-тип operator=(список-параметров-не-по-умолчанию ) = default; (3) (начиная с C++11)
возвращаемый-тип operator=(список-параметров ) = delete; (4) (начиная с C++11)
возвращаемый-тип имя-класса ::operator=(список-параметров ) тело-функции (5)
возвращаемый-тип имя-класса ::operator=(список-параметров-не-по-умолчанию ) = default; (6) (начиная с C++11)
имя-класса класс, для которого объявляется оператор присваивания копированием, в описаниях ниже тип класса указывается как T
список-параметров список параметров только из одного параметра, который имеет тип T, T&, const T&, volatile T& или const volatile T&
список-параметров-не-по-умолчанию список параметров только из одного параметра, который имеет тип T, T&, const T&, volatile T& или const volatile T& и не имеет аргумента по умолчанию
тело-функции тело функции оператора присваивания копированием
возвращаемый-тип любой тип, но предпочтение отдается T&, чтобы можно было выполнять цепочки присваиваний

[править] Объяснение

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

Из-за этих правил неявно объявленный оператор присваивания копированиtv не может привязываться к аргументу volatile lvalue.

Класс может иметь несколько операторов присваивания копированием, например как T& T::operator=(T&) так и T& T::operator=(T). Если присутствуют некоторые пользовательские операторы присваивания копированием, пользователь всё равно может принудительно сгенерировать неявно объявленный оператор присваивания копированием ключевым словом default. (начиная с C++11)

Неявно объявленный (или заданный по умолчанию при первом объявлении) оператор присваивания копированием имеет спецификацию исключения, как описано в спецификации динамических исключений (до C++17)спецификации noexcept (начиная с C++17)

Поскольку оператор присваивания копированием всегда объявляется для любого класса, оператор присваивания базового класса всегда скрыт. Если для ввода оператора присваивания из базового класса используется using-объявление и его тип аргумента может быть таким же, как тип аргумента неявного оператора присваивания производного класса, using-объявление также скрыто неявным объявлением.

[править] Неявно определённый оператор присваивания копированием

Если неявно объявленный оператор присваивания копированием не является ни удалённым, ни тривиальным, он определяется (то есть тело функции генерируется и компилируется) компилятором, если используется odr или это необходимо для константной оценки (начиная с C++14). Для типов объединения неявно определённое присваивание копированием копирует представление объекта (как std::memmove). Для классовых типов не объединений (class и struct) оператор выполняет поэлементное копирование базовых и нестатических элементов объекта в порядке их инициализации, используя встроенное присваивание для скаляров и оператор присваивания копированием для классовых типов.

Неявно определённый оператор присваивания копированием для класса T является constexpr если

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

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

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

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

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

[править] Удалённый оператор присваивания копированием

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

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

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

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

[править] Тривиальный оператор присваивания копированием

Оператор присваивания копированием для класса T тривиален, если выполняются все следующие условия:

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

Тривиальный оператор присваивания копированием создаёт копию представления объекта, как если при выполнении std::memmove. Все типы данных, совместимые с языком C (типы POD), тривиально присваиваются копированием.

[править] Доступный оператор присваивания копированием

Оператор присваивания копированием доступен, если он либо объявлен пользователем, либо неявно объявлен и может быть определён.

(до C++11)

Оператор присваивания копированием доступен, если он не удалён.

(начиная с C++11)
(до C++20)

Оператор присваивания копированием доступен, если

  • он не удаляется и
  • его связанные ограничения, если таковые имеются, выполнены, и
  • никакой оператор присваивания копированием с тем же типом первого параметра и теми же cv/ссылочным квалификаторами (если они есть) не является более ограниченным, чем он.
(начиная с C++20)

Тривиальность подходящих операторов присваивания копированием определяет, является ли класс тривиально копируемым типом.

[править] Примечание

Если предоставлены операторы присваивания копированием и перемещением, разрешение перегрузки выбирает присваивание перемещением, если аргумент является rvalue (или prvalue, например безымянное временное значение или xvalue, например результат std::move), и выбирает присваивание копированием, если аргумент является lvalue (именованный объект или функция/оператор, возвращающая ссылку на lvalue). Если обеспечено только присваивание копированием, все категории аргументов выбирают его (при условии, что аргумент принимается по значению или как ссылка на const, поскольку значения rvalue могут привязываться к ссылкам const), что делает присваивание копированием запасным вариантом для присваивания перемещением, когда перемещение недоступно.

Не указано, назначаются ли виртуальные подобъекты базового класса, доступные более чем через один путь в решётке наследования, более одного раза с помощью неявно определённого оператора присваивания копированием (то же самое относится к присваиванию перемещением).

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

[править] Пример

#include <algorithm>
#include <iostream>
#include <memory>
#include <string>
 
struct A
{
    int n;
    std::string s1;
    A() = default;
    A(A const&) = default;
    // определяемое пользователем присваивание копированием (идиома копирования и замены)
    A& operator=(A other)
    {
        std::cout << "присваивание копированием А\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;
    // определяемое пользователем присваивание копированием (не идиома копирования и
    // замены)
    // примечание: копирование и замена всегда приводит к перераспределению ресурсов
    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 вызывает ";
    a1 = a2; // определяемое пользователем присваивание копированием
 
    B b1, b2;
    b2.s1 = "foo";
    b2.s2 = "bar";
    std::cout << "b1 = b2 вызывает ";
    b1 = b2; // неявно определённое присваивание копированием
    std::cout << "b1.s1 = " << b1.s1 << "; b1.s2 = " << b1.s2 << '\n';
}

Вывод:

a1 = a2 вызывает присваивание копированием A
b1 = b2 вызывает присваивание копированием A
b1.s1 = foo; b1.s2 = bar

[править] Отчёты о дефектах

Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:

Номер Применён Поведение в стандарте Корректное поведение
CWG 1353 C++98 условия, при которых неявно объявленные операторы присваивания
копированием не определены, не учитывали типы многомерных массивов
эти типы рассмотрены
CWG 2094 C++11 volatile подобъект делает нетривиальными операторы присваивания
копированием по умолчанию (CWG проблема 496)
тривиальность не затрагивается
CWG 2171 C++11 operator=(X&) = default был нетривиальным сделан тривиальным
CWG 2180 C++11 оператор присваивания копированием по умолчанию для класса T не
определялся удалённым, если T являлся абстрактным и имел не
подлежащие копированию прямые виртуальные базовые классы
в этом случае оператор определяется
как удалённый

[править] Смотрите также