Пространства имён
Варианты
Действия

Инициализация копированием

Материал из cppreference.com
< cpp‎ | language
 
 
 
 

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

Содержание

[править] Синтаксис

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, это классифицируется как инициализация списком, а сужающее преобразование не разрешено).
3) Когда передача аргумента в функцию выполняется по значению
4) Когда происходит возврат из функции, которая возвращает по значению
5) Когда выбрасывание или перехват исключения происходит по значению
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

[править] Смотрите также