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
的具名变量(自动、静态或线程局部)时,其初始化器由等号后跟表达式构成。
复制初始化的效果是:
| (since C++17) |
-
首先
(C++17 前)
否则
(C++17 起)
,若
T是类类型且 other 类型的 cv 未限定版本是T或派生自T的类,则检查T的 非显式构造函数 并通过重载决议选择最佳匹配。随后调用该构造函数来初始化对象。
-
否则,若
T是类类型,且 other 类型的 cv 未限定版本既非T亦非派生自T,或若T是非类类型但 other 的类型是类类型,则考察能从 other 的类型转换到T(或当T是类类型且存在转换函数时,转换到派生自T的类型)的 用户定义转换序列 ,并通过重载决议选择最佳序列。转换结果(若使用 转换构造函数 则生成T的 cv 未限定版本的 右值临时对象 (C++11 前) 纯右值临时对象 (C++11 起) (C++17 前) 纯右值表达式 (C++17 起) )随后用于 直接初始化 该对象。 最后一步通常会被 优化消除 ,转换结果将直接构造在为目标对象分配的内存中,但即使未被使用,仍需可访问对应的构造函数(移动或复制构造函数)。 (C++17 前)
-
否则(如果
T和 other 的类型均非类类型),则在必要时使用 标准转换 将 other 的值转换为T的 cv 非限定版本。
注释
复制初始化比直接初始化更严格: 显式构造函数 不是 转换构造函数 ,在复制初始化中不会被考虑。
struct Exp { explicit Exp(const char*) {} }; // 不可从 const char* 隐式转换 Exp e1("abc"); // 正确 Exp e2 = "abc"; // 错误:拷贝初始化不考虑 explicit 构造函数 struct Imp { Imp(const char*) {} }; // 可从 const char* 隐式转换 Imp i1("abc"); // 正确 Imp i2 = "abc"; // 正确
此外,复制初始化中的隐式转换必须直接从初始化式生成
T
,而直接初始化则要求从初始化式到
T
构造函数参数的隐式转换。
struct S { S(std::string) {} }; // 可从 std::string 隐式转换 S s("abc"); // 正确:从 const char[4] 到 std::string 的转换 S s = "abc"; // 错误:无法从 const char[4] 转换到 S S s = "abc"s; // 正确:从 std::string 到 S 的转换
如果 other 是右值表达式,将通过重载决议选择并调用 move constructor 进行拷贝初始化。这种情况仍被视为拷贝初始化;没有专门的术语(例如移动初始化)来描述这种情况。
隐式转换
是通过拷贝初始化的方式定义的:如果一个类型为
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"; // 正确:构造函数为非显式 std::string s2 = std::move(s); // 此拷贝初始化执行移动操作 // std::unique_ptr<int> p = new int(1); // 错误:构造函数为显式 std::unique_ptr<int> p(new int(1)); // 正确:直接初始化 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++标准。
| 缺陷报告 | 适用范围 | 发布时行为 | 正确行为 |
|---|---|---|---|
| CWG 5 | C++98 |
目标类型的cv限定符会应用于
通过转换构造函数初始化的临时对象 |
临时对象不应具有cv限定 |
| CWG 177 | C++98 |
类对象拷贝初始化过程中创建的临时对象
值类别未作规定 |
明确规定为右值 |