Инициализация копированием
Инициализирует объект из другого объекта.
Содержание |
[править] Синтаксис
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(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 |