Namespaces
Variants

Copy-initialization

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

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

Содержание

Синтаксис

T object = other ; (1)
T object = { other }; (2) (до C++11)
f ( other ) (3)
return other ; (4)
throw object ;

catch ( T object )

(5)
T array [ N ] = { other-sequence }; (6)

Объяснение

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

1) Когда именованная переменная (автоматическая, статическая или локальная для потока) нессылочного типа T объявляется с инициализатором, состоящим из знака равенства, за которым следует выражение.
2) (until C++11) Когда именованная переменная скалярного типа T объявляется с инициализатором, состоящим из знака равенства, за которым следует выражение в фигурных скобках (Примечание: начиная с C++11, это классифицируется как list initialization , и сужающие преобразования не допускаются).
3) При передаче аргумента в функцию по значению.
4) При возврате из функции, которая возвращает значение по значению.
5) При выбрасывании или перехвате исключения по значению.
6) В рамках aggregate initialization , для инициализации каждого элемента, для которого предоставлен инициализатор.

Эффекты копирующей инициализации:

  • Во-первых, если T является классом и инициализатор представляет собой prvalue выражение, тип без cv-квалификаторов которого совпадает с классом T , то для инициализации целевого объекта используется само выражение инициализатора, а не временный объект, созданный из него: см. copy elision .
(начиная с C++17)
  • Первый (до C++17) Иначе (начиная с C++17) , если T является классом и версия типа other без cv-квалификаторов является 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 .

Примечания

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

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

Если other является rvalue-выражением, move constructor будет выбран разрешением перегрузки и вызван во время copy-initialization. Это всё ещё считается copy-initialization; для этого случая нет специального термина (например, move-initialization).

Неявное преобразование определяется в терминах копирующей инициализации: если объект типа 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: конструктор не является explicit
    std::string s2 = std::move(s); // эта copy-инициализация выполняет перемещение
//  std::unique_ptr<int> p = new int(1); // ошибка: конструктор является explicit
    std::unique_ptr<int> p(new int(1));  // OK: прямая инициализация
    int n = 3.14;    // преобразование с плавающей точкой в целочисленное
    const int b = n; // const не имеет значения
    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++.

DR Applied to Behavior as published Correct behavior
CWG 5 C++98 the cv-qualification of the destination type is applied to
the temporary initialized by a converting constructor
the temporary is not cv-qualified
CWG 177 C++98 the value category of the temporary created during
copy-initialization of a class object is unspecified
specified as rvalue

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