Spec-Zone.ru › C++

Копирующая инициализация

Инициализирует объект другим объектом.

Синтаксис

T object = other; (1)
T object = {other}; (2) (до C++11)
f(other) (3)
return other; (4)
throw object;

catch (T object)

(5)
T array[N] = {other-sequence}; (6)

Пояснение

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

1) При объявлении именованной переменной (автоматической, статической или локальной для потока) нереференсного типа T с инициализатором, состоящим из знака равенства, за которым следует выражение.
2) (до C++11) При объявлении именованной переменной скалярного типа T с инициализатором, состоящим из знака равенства, за которым следует выражение в фигурных скобках (Примечание: начиная с C++11 это классифицируется как инициализация списком, при этом сужающее преобразование не допускается).
3) При передаче аргумента функции по значению.
4) При возврате из функции, возвращающей значение.
5) При выбрасывании или перехвате исключения по значению.
6) В составе агрегатной инициализации для инициализации каждого элемента, для которого задан инициализатор.

Эффекты копирующей инициализации:

  • Сначала, если T является типом класса, а инициализатор — выражением prvalue, cv-неквалифицированный тип которого совпадает с классом T, для инициализации целевого объекта используется само выражение-инициализатор, а не созданный из него временный объект: см. устранение копирования.
(начиная с C++17)
  • В противном случае, если T является типом класса, а cv-неквалифицированная версия типа other — это T или класс, производный от T, рассматриваются неявные конструкторы класса T, а наиболее подходящий выбирается посредством разрешения перегрузки. Затем этот конструктор вызывается для инициализации объекта.
  • В противном случае, если T является типом класса, а cv-неквалифицированная версия типа other не является T и не является производным от T, или если T не является типом класса, но тип other является типом класса, рассматриваются последовательности пользовательских преобразований, способные преобразовать тип other в T (или в тип, производный от T, если T является типом класса и доступна функция преобразования), и наиболее подходящая выбирается посредством разрешения перегрузки. Результат преобразования — временный объект rvalue(до C++11)временный объект prvalue(начиная с C++11)(до C++17)выражение prvalue(начиная с C++17) cv-неквалифицированной версии T, если использовался преобразующий конструктор, затем используется для прямой инициализации объекта. Последний шаг обычно оптимизируется, и результат преобразования создаётся непосредственно в памяти, выделенной для целевого объекта, однако соответствующий конструктор (перемещения или копирования) должен быть доступен, даже если он не используется.(до C++17)
  • В противном случае (если ни T, ни тип other не являются типами классов) для преобразования значения other в cv-неквалифицированную версию T при необходимости используются стандартные преобразования.

Примечания

Копирующая инициализация менее разрешительна, чем прямая инициализация: явные конструкторы не являются преобразующими конструкторами и не рассматриваются при копирующей инициализации.

struct Exp { explicit Exp(const char*) {} }; // not convertible from const char*
Exp e1("abc");  // OK
Exp e2 = "abc"; // Error, copy-initialization does not consider explicit constructor

struct Imp { Imp(const char*) {} }; // convertible from const char*
Imp i1("abc");  // OK
Imp i2 = "abc"; // OK

Кроме того, при копирующей инициализации неявное преобразование должно непосредственно создавать T из инициализатора, тогда как, например, при прямой инициализации ожидается неявное преобразование инициализатора в аргумент конструктора T.

struct S { S(std::string) {} }; // implicitly convertible from std::string
S s("abc");   // OK: conversion from const char[4] to std::string
S s = "abc";  // Error: no conversion from const char[4] to S
S s = "abc"s; // OK: conversion from std::string to S

Если other является выражением rvalue, разрешение перегрузки выберет конструктор перемещения, который будет вызван при копирующей инициализации. Это по-прежнему считается копирующей инициализацией; для этого случая нет специального термина (например, «инициализация перемещением»).

Неявное преобразование определяется через копирующую инициализацию: если объект типа T можно скопировать инициализировать выражением E, то E неявно преобразуется в T.

Знак равенства = при копирующей инициализации именованной переменной не связан с оператором присваивания. Перегрузки оператора присваивания не влияют на копирующую инициализацию.

Пример

#include <memory>
#include <string>
#include <utility>
 
struct A
{
    operator int() { return 12;}
};
 
struct B
{
    B(int) {}
};
 
int main()
{
    std::string s = "test";        // OK: constructor is non-explicit
    std::string s2 = std::move(s); // this copy-initialization performs a move
 
//  std::unique_ptr<int> p = new int(1); // error: constructor is explicit
    std::unique_ptr<int> p(new int(1));  // OK: direct-initialization
 
    int n = 3.14;    // floating-integral conversion
    const int b = n; // const doesn't matter
    int c = b;       // ...either way
 
    A a;
    B b0 = 12;
//  B b1 = a;       // < error: conversion from 'A' to non-scalar type 'B' requested
    B b2{a};        // < identical, calling A::operator int(), then B::B(int)
    B b3 = {a};     // <
    auto b4 = B{a}; // <
    
//  b0 = a;         // < error, assignment operator overload needed

    [](...){}(c, b0, b3, b4); // pretend these variables are used
}

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

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

DR Применён к Опубликованное поведение Корректное поведение
CWG 5 C++98 cv-квалификация целевого типа применяется к
временному объекту, инициализированному преобразующим конструктором
временный объект не имеет cv-квалификации
CWG 177 C++98 категория значения временного объекта, созданного при
копирующей инициализации объекта класса, не определена
определена как rvalue

См. также

  • устранение копирования
  • преобразующий конструктор
  • копирующее присваивание
  • конструктор копирования
  • конструктор по умолчанию
  • деструктор
  • explicit
  • инициализация
    • агрегатная инициализация
    • константная инициализация
    • инициализация по умолчанию
    • прямая инициализация
    • список инициализаторов
    • инициализация списком
    • инициализация ссылки
    • инициализация значения
    • инициализация нулём
  • перемещающее присваивание
  • конструктор перемещения
  • new

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/copy_initialization

Spec-Zone.ru

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