Namespaces
Variants

Copy-initialization

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

从另一个对象初始化对象。

目录

语法

T object = other ; (1)
T object = { other }; (2) (C++11 前)
f ( other ) (3)
return other ; (4)
throw object ;

catch ( T object )

(5)
T array [ N ] = { other-sequence }; (6)

解释

复制初始化在以下情况下执行:

1) 当声明一个非引用类型 T 的具名变量(自动、静态或线程局部)时,其初始化器由等号后跟表达式构成。
2) (until C++11) 当声明一个标量类型 T 的命名变量,且初始化器由等号后接花括号括起的表达式组成时(注意:自 C++11 起,这被归类为 列表初始化 ,且不允许窄化转换)。
3) 当通过值传递 参数 给函数时。
4) 当从 按值返回 的函数中返回时。
5) 当通过值 抛出 捕获 异常时。
6) 作为 聚合初始化 的一部分,用于初始化每个提供了初始化器的元素。

复制初始化的效果是:

  • 首先,如果 T 是类类型且初始化器是一个其 cv 非限定类型与 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 类对象拷贝初始化过程中创建的临时对象
值类别未作规定
明确规定为右值

另请参阅