Spec-Zone.ru › C++

Конструкторы копирования

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

Синтаксис

class-name (parameter-list ); (1)
class-name (parameter-list ) function-body (2)
class-name (single-parameter-list ) = default; (3) (начиная с C++11)
class-name (parameter-list ) = delete; (4) (начиная с C++11)
class-name ::class-name (parameter-list ) function-body (5)
class-name ::class-name (single-parameter-list ) = default; (6) (начиная с C++11)
class-name - класс, конструктор копирования которого объявляется
parameter-list - непустой список параметров, удовлетворяющий всем следующим условиям:
  • если тип класса обозначить как T, первый параметр имеет тип T&, constT&, volatileT& или constvolatileT&, и
  • либо других параметров нет, либо у всех остальных параметров есть аргументы по умолчанию.
single-parameter-list - список параметров только с одним параметром типа T&, constT&, volatileT& или constvolatileT&, у которого нет аргумента по умолчанию
function-body - тело функции конструктора копирования

Пояснение

1) Объявление конструктора копирования внутри определения класса.
2-4) Определение конструктора копирования внутри определения класса.
3) Конструктор копирования явно задан по умолчанию.
4) Конструктор копирования удалён.
5,6) Определение конструктора копирования вне определения класса (класс должен содержать объявление (1)).
6) Конструктор копирования явно задан по умолчанию.
struct X
{
    X(X& other); // copy constructor
//  X(X other);  // Error: incorrect parameter type
};

union Y
{
    Y(Y& other, int num = 1); // copy constructor with multiple parameters
//  Y(Y& other, int num);     // Error: `num` has no default argument
};

Конструктор копирования вызывается всякий раз, когда объект инициализируется (прямой инициализацией или инициализацией копированием) из другого объекта того же типа (если только разрешение перегрузки не выбирает более подходящий вариант или вызов не устраняется); сюда входят

  • инициализация: T a = b; или T a(b);, где b имеет тип T;
  • передача аргумента функции: f(a);, где a имеет тип T, а f является void f(T t);
  • возврат из функции: return a; внутри такой функции, как T f(), где a имеет тип T, у которого нет конструктора перемещения.

Неявно объявленный конструктор копирования

Если для типа класса не предоставлены пользовательские конструкторы копирования, компилятор всегда объявляет конструктор копирования как не-explicit-член класса с доступом inlinepublic. Этот неявно объявленный конструктор копирования имеет форму T::T(const T&), если выполняются все следующие условия:

  • у каждой прямой и виртуальной базовой B класса T есть конструктор копирования, параметры которого имеют тип constB& или constvolatileB&;
  • у каждого нестатического члена данных M класса T, имеющего тип класса или массив класса, есть конструктор копирования, параметры которого имеют тип constM& или constvolatileM&.

В противном случае неявно объявленный конструктор копирования имеет форму T::T(T&).

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

У класса может быть несколько конструкторов копирования, например, и T::T(const T&), и T::T(T&).

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

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

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

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

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

Если это удовлетворяет требованиям к конструктору constexpr(до C++23)функции constexpr(начиная с C++23), сгенерированный конструктор копирования является constexpr.

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

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

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

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

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

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

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

Тривиальный конструктор копирования

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

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

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

Объекты типа TriviallyCopyable можно копировать вручную, копируя их представления объектов, например, с помощью std::memmove. Все типы данных, совместимые с языком C (типы POD), являются тривиально копируемыми.

Подходящий конструктор копирования

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

(до C++11)

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

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

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

  • Он не удалён.
  • Его связанные ограничения (если есть) выполнены.
  • Нет другого конструктора копирования с выполненными связанными ограничениями, который был бы более ограниченным.
(начиная с C++20)

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

Примечания

Во многих ситуациях вызовы конструкторов копирования оптимизируются даже в том случае, если они могли бы иметь наблюдаемые побочные эффекты; см. устранение копирования.

Пример

struct A
{
    int n;
    A(int n = 1) : n(n) {}
    A(const A& a) : n(a.n) {} // user-defined copy constructor
};

struct B : A
{
    // implicit default constructor B::B()
    // implicit copy constructor B::B(const B&)
};

struct C : B
{
    C() : B() {}
private:
    C(const C&); // non-copyable, C++98 style
};

int main()
{
    A a1(7);
    A a2(a1); // calls the copy constructor
    
    B b;
    B b2 = b;
    A a3 = b; // conversion to A& and copy constructor
    
    volatile A va(10);
    // A a4 = va; // compile error
    
    C c;
    // C c2 = c; // compile error
}

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

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

Отчёт об ошибке Применён к Опубликованное поведение Правильное поведение
CWG 1353 C++98 условия, при которых неявно объявленные конструкторы копирования
не определялись, не учитывали многомерные массивы
учитывать такие типы
CWG 2094 C++11 члены volatile делают копирование нетривиальным (отчёт CWG 496) тривиальность не затрагивается
CWG 2171 C++11 X(X&) = default был нетривиальным сделан тривиальным
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/copy_constructor

Spec-Zone.ru

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