Copy-initialization
Инициализирует объект из другого объекта.
Содержание |
Синтаксис
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, это классифицируется как
list initialization
, и сужающие преобразования не допускаются).
Эффекты копирующей инициализации:
|
(начиная с 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 |