复制初始化
来自 cppreference.cn
用另一个对象初始化一个对象。
目录 |
[编辑] 语法
T 对象 = 其他; |
(1) | ||||||||
T 对象 = { 其他}; |
(2) | (C++11 前) | |||||||
f( 其他) |
(3) | ||||||||
return 其他; |
(4) | ||||||||
throw 对象;
|
(5) | ||||||||
T 数组[ N] = { 其他序列}; |
(6) | ||||||||
[编辑] 解释
复制初始化在以下情况中执行:
1) 当声明一个非引用类型
T
的具名变量(自动、静态或线程局部),其初始化器由一个等号后跟一个表达式组成时。3) 当通过值传递参数给函数时。
4) 当从按值返回的函数中返回时。
6) 作为聚合初始化的一部分,用于初始化每个提供了初始化器的元素。
复制初始化的效果是:
(C++17 起) |
- 否则,如果
T
是一个类类型,并且 其他 的类型的 cv-unqualified 版本是T
或从T
派生的类,则检查T
的非 explicit 构造函数,并通过重载决议选择最佳匹配。然后调用该构造函数来初始化对象。
- 否则,如果
T
是一个类类型,并且 其他 的类型的 cv-unqualified 版本不是T
或从T
派生,或者如果T
是非类类型,但 其他 的类型是类类型,则检查可以将 其他 的类型转换为T
(如果T
是类类型且存在转换函数,则转换为从T
派生的类型)的用户定义转换序列,并通过重载决议选择最佳序列。转换的结果,如果使用了转换构造函数,它是一个 cv-unqualified 版本的T
的右值临时对象(C++11 前)prvalue 临时对象(C++11 起)(C++17 前)prvalue 表达式(C++17 起),然后用于直接初始化对象。最后一步通常被优化掉,转换的结果直接构造到为目标对象分配的内存中,但即使不使用,也需要访问适当的构造函数(移动或复制)。(C++17 前)
- 否则(如果
T
和 其他 的类型都不是类类型),如有必要,使用标准转换将 其他 的值转换为T
的 cv-unqualified 版本。
[编辑] 注意
复制初始化不如直接初始化宽松:explicit 构造函数不是转换构造函数,不考虑用于复制初始化。
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++ 标准。
缺陷报告 | 应用于 | 发布时的行为 | 正确的行为 |
---|---|---|---|
CWG 5 | C++98 | 目标类型的 cv-限定符应用于 由转换构造函数初始化的临时对象 |
临时对象未 cv-限定 |
CWG 177 | C++98 | 在类对象复制初始化期间创建的临时对象的 值类别未指定 |
指定为右值 |