复制初始化
来自 cppreference.cn
从另一个对象初始化对象。
内容 |
[编辑] 语法
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) | ||||||||
[编辑] 解释
在下列情境中执行复制初始化
1) 当声明一个非引用类型
T
的具名变量(自动、静态或线程局部),且初始化器由等号后随一个表达式构成时。3) 当按值将实参传递给函数时。
4) 当从一个按值返回的函数返回时。
6) 作为聚合初始化的一部分,初始化提供初始化器的每个元素。
复制初始化的效果是
(自 C++17) |
- 否则,若
T
是类类型,且 other 的类型的 cv 无限定版本是T
或派生自T
的类,则检验T
的非显式构造函数,并通过重载决议选择最佳匹配。然后调用该构造函数以初始化对象。
- 否则,若
T
是类类型,且 other 的类型的 cv 无限定版本非T
或派生自T
,或若T
是非类类型,但 other 的类型是类类型,则检验能从 other 的类型转换到T
(或若T
是类类型且转换函数可用则转换到派生自T
的类型)的用户定义转换序列,并通过重载决议选择最佳者。转换的结果,若使用了转换构造函数,则为 cv 无限定版本T
的右值临时量(直到 C++11)纯右值临时量(自 C++11)(直到 C++17)纯右值表达式(自 C++17),然后用于直接初始化对象。最后一步通常被优化掉,且转换结果被直接构造到为目标对象分配的内存中,但仍要求可访问适当的构造函数(移动或复制),即使它未被使用。(直到 C++17)
- 否则(若
T
和 other 的类型都不是类类型),则必要时使用标准转换,以将 other 的值转换为T
的 cv 无限定版本。
[编辑] 注解
复制初始化比直接初始化限制更多:显式构造函数不是转换构造函数,且不为复制初始化所考虑。
struct Exp { explicit Exp(const char*) {} }; // not convertible from const char* Exp e1("abc"); // OK Exp e2 = "abc"; // Error, copy-initialization does not consider explicit constructor struct Imp { Imp(const char*) {} }; // convertible from const char* Imp i1("abc"); // OK Imp i2 = "abc"; // OK
此外,复制初始化中的隐式转换必须直接从初始化器产生 T
,而例如,直接初始化期望从初始化器到 T
的构造函数的实参的隐式转换。
struct S { S(std::string) {} }; // implicitly convertible from std::string S s("abc"); // OK: conversion from const char[4] to std::string S s = "abc"; // Error: no conversion from const char[4] to S S s = "abc"s; // OK: conversion from std::string to S
若 other 是右值表达式,则将通过重载决议选择移动构造函数并在复制初始化期间调用它。这仍被认为是复制初始化;此情况下没有特殊术语(例如,移动初始化)。
隐式转换是按复制初始化定义的:若能用表达式 E
复制初始化类型 T
的对象,则 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: constructor is non-explicit std::string s2 = std::move(s); // this copy-initialization performs a move // std::unique_ptr<int> p = new int(1); // error: constructor is explicit std::unique_ptr<int> p(new int(1)); // OK: direct-initialization int n = 3.14; // floating-integral conversion const int b = n; // const doesn't matter int c = b; // ...either way A a; B b0 = 12; // B b1 = a; // < error: conversion from 'A' to non-scalar type 'B' requested B b2{a}; // < identical, calling A::operator int(), then B::B(int) B b3 = {a}; // < auto b4 = B{a}; // < // b0 = a; // < error, assignment operator overload needed [](...){}(c, b0, b3, b4); // pretend these variables are used }
[编辑] 缺陷报告
下列行为更改缺陷报告被追溯应用到先前发布的 C++ 标准。
DR | 应用于 | 已发布行为 | 正确行为 |
---|---|---|---|
CWG 5 | C++98 | 目标类型的 cv 限定应用于 由转换构造函数初始化的临时量 |
临时量不是 cv 限定的 |
CWG 177 | C++98 | 期间创建的临时量的求值类别 类对象的复制初始化是未指明的 |
指明为右值 |