Копирующая инициализация
Инициализирует объект другим объектом.
Синтаксис
T object = other; | (1) | |
T object = {other}; | (2) | (до C++11) |
f(other) | (3) | |
return other; | (4) | |
throw object;
| (5) | |
T array[N] = {other-sequence}; | (6) |
Пояснение
Копирующая инициализация выполняется в следующих ситуациях:
T с инициализатором, состоящим из знака равенства, за которым следует выражение.T с инициализатором, состоящим из знака равенства, за которым следует выражение в фигурных скобках (Примечание: начиная с C++11 это классифицируется как инициализация списком, при этом сужающее преобразование не допускается).Эффекты копирующей инициализации:
| (начиная с 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