复制初始化
来自 cppreference.com
从另一个对象初始化一个对象。
目录 |
[编辑] 语法
T object = other; |
(1) | ||||||||
T 对象 = { 其他}; |
(2) | (直至 C++11) | |||||||
f( 其他) |
(3) | ||||||||
return 其他; |
(4) | ||||||||
throw 对象;
|
(5) | ||||||||
T 数组[ N] = { 其他序列}; |
(6) | ||||||||
[编辑] 说明
在以下情况下执行复制初始化
1) 当使用由等号和表达式组成的初始化器声明非引用类型
T
的命名变量(自动、静态或线程局部)时。3) 当 按值传递参数给函数时。
4) 当从按值返回的函数 返回 时。
6) 作为 聚合初始化 的一部分,初始化为其提供了初始化器的每个元素。
复制初始化的效果是
(自 C++17 起) |
- 否则,如果
T
是类类型,并且 其他 类型的 cv 非限定版本是T
或从T
派生的类,则检查T
的 非显式构造函数,并通过重载决议选择最佳匹配。然后调用该构造函数来初始化对象。
- 否则,如果
T
是类类型,并且 其他 类型的 cv 非限定版本不是T
或从T
派生,或者如果T
是非类类型,但 其他 的类型是类类型,则检查可以从 其他 的类型转换为T
(或者如果T
是类类型并且转换函数可用,则转换为从T
派生的类型)的 用户定义的转换序列,并通过重载决议选择最佳的一个。转换的结果(如果使用了 转换构造函数,则为T
的 cv 非限定版本的 右值临时对象(直至 C++11)纯右值临时对象(自 C++11 起)(直至 C++17)纯右值表达式(自 C++17 起))然后用于 直接初始化 对象。 最后一步通常会被 优化,转换结果直接在为目标对象分配的内存中构造,但即使不使用适当的构造函数(移动或复制),也需要可以访问它。(直至 C++17)
- 否则(如果
T
和 其他 的类型都不是类类型),则在必要时使用 标准转换 将 其他 的值转换为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
如果 其他 是一个右值表达式,则在复制初始化期间,将通过重载决议选择并调用 移动构造函数。这仍然被视为复制初始化;对于这种情况,没有特殊的术语(例如,移动初始化)。
隐式转换 是根据复制初始化定义的:如果类型为 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: 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 | 类对象复制初始化期间创建的临时对象的 值类别 未指定 复制初始化期间创建的临时对象的 值类别 未指定 |
指定为右值 |