◐ Shell
clean mode source ↗

Инициализация копированием — cppreference.com

Материал из cppreference.com

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

Синтаксис

T объект = другой; (1)
T объект = {другой}; (2) (до C++11)
f(другой) (3)
return другой; (4)
throw объект;

catch (T объект)

(5)
T массив[N] = {другая-последовательность}; (6)

Объяснение

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

1) Когда именованная переменная (автоматическая, статическая или локальная для потока) нессылочного типа T объявляется с инициализатором, состоящим из знака равенства, за которым следует выражение.

2) (до C++11) Когда именованная переменная скалярного типа T объявляется с инициализатором, состоящим из знака равенства, за которым следует заключённое в фигурные скобки выражение (Примечание: начиная с C++11, это классифицируется как инициализация списком, а сужающее преобразование не разрешено).

4) Когда происходит возврат из функции, которая возвращает по значению

6) Как часть агрегатной инициализация для инициализации каждого элемента, для которого предоставляется инициализатор

Эффекты инициализации копированием:

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

Примечание

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

struct Exp { explicit Exp(const char*) {} }; // не конвертирует из const char*
Exp e1("abc");  // OK
Exp e2 = "abc"; // Ошибка, инициализация копированием не учитывает явный конструктор

struct Imp { Imp(const char*) {} }; // конвертирует из const char*
Imp i1("abc");  // OK
Imp i2 = "abc"; // OK

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

struct S { S(std::string) {} }; // неявно конвертируется из std::string
S s("abc");   // OK: преобразование из const char[4] в std::string
S s = "abc";  // Ошибка: нет преобразования из const char[4] в S
S s = "abc"s; // OK: преобразование из std::string в S

Если другой является выражением 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: конструктор не является явным
    std::string s2 = std::move(s); // эта инициализация копированием выполняет перемещение
 
//  std::unique_ptr<int> p = new int(1); // ошибка: конструктор явный
    std::unique_ptr<int> p(new int(1));  // OK: прямая инициализация
 
 
    int n = 3.14;    // преобразование значения с плавающую запятой в целое
    const int b = n; // константа не имеет значения
    int c = b;       // ...другой способ
 
 
    A a;
    B b0 = 12;
//  B b1 = a;       // < ошибка: запрошено преобразование из 'A' в нескалярный тип 'B'
    B b2{a};        // < идентично вызову A::operator int(), затем B::B(int)
    B b3 = {a};     // <
    auto b4 = B{a}; // <
    
//  b0 = a;         // < ошибка, требуется перегрузка оператора присваивания
    [](...){}(c, b0, b3, b4); // сделать вид, что эти переменные используются
}

Отчёты о дефектах

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

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

Смотрите также