命名空间
变体
操作

复制初始化

来自 cppreference.com
< cpp‎ | 语言
 
 
C++ 语言
 
 

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

目录

[编辑] 语法

T object = other; (1)
T 对象 = {其他}; (2) (直至 C++11)
f(其他) (3)
return 其他; (4)
throw 对象;

catch (T 对象)

(5)
T 数组[N] = {其他序列}; (6)

[编辑] 说明

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

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

复制初始化的效果是

  • 首先,如果 T 是类类型,并且初始化器是一个 cv 非限定类型与 T 相同的 纯右值 表达式,则使用初始化器表达式本身(而不是由其物化的临时对象)来初始化目标对象:参见 复制省略
(自 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 类对象复制初始化期间创建的临时对象的 值类别 未指定
复制初始化期间创建的临时对象的 值类别 未指定
指定为右值

[编辑] 另请参阅