Инициализация копированием
Инициализирует объект из другого объекта.
Содержание |
[править]Синтаксис
Tобъект= другой; | (1) | ||||||||
Tобъект= { другой}; | (2) | (до C++11) | |||||||
f( другой) | (3) | ||||||||
return другой; | (4) | ||||||||
throw объект;
| (5) | ||||||||
Tмассив[ N] = { другая-последовательность}; | (6) | ||||||||
[править]Объяснение
Инициализация копированием выполняется в следующих случаях:
T
объявляется с инициализатором, состоящим из знака равенства, за которым следует выражение.T
объявляется с инициализатором, состоящим из знака равенства, за которым следует заключённое в фигурные скобки выражение (Примечание: начиная с C++11, это классифицируется как инициализация списком, а сужающее преобразование не разрешено).Эффекты инициализации копированием:
| (начиная с 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(constchar*){}};// не конвертирует из const char* Exp e1("abc");// OK Exp e2 ="abc";// Ошибка, инициализация копированием не учитывает явный конструктор struct Imp { Imp(constchar*){}};// конвертирует из 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(){return12;}}; 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;// преобразование значения с плавающую запятой в целоеconstint 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 |