命名空间
变体
操作

复制初始化

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

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

内容

[编辑] 语法

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) (直到 C++11) 当声明一个标量类型 T 的具名变量,且初始化器由等号后随一个花括号包围的表达式构成时。(注意:自 C++11 起,这被归类为列表初始化,且不容许窄化转换)。
3)按值将实参传递给函数时。
4) 当从一个按值返回的函数返回时。
5)抛出捕获按值传递的异常时。
6) 作为聚合初始化的一部分,初始化提供初始化器的每个元素。

复制初始化的效果是

  • 首先,若 T 是类类型且初始化器是 cv 无限定类型与 T 相同的纯右值表达式,则使用初始化器表达式自身,而非从中具现化的临时量,以初始化目标对象:见复制消除
(自 C++17)
  • 否则,若 T 是类类型,且 other 的类型的 cv 无限定版本是 T 或派生自 T 的类,则检验 T非显式构造函数,并通过重载决议选择最佳匹配。然后调用该构造函数以初始化对象。
  • 否则,若 T 是类类型,且 other 的类型的 cv 无限定版本非 T 或派生自 T,或若 T 是非类类型,但 other 的类型是类类型,则检验能从 other 的类型转换到 T (或若 T 是类类型且转换函数可用则转换到派生自 T 的类型)的用户定义转换序列,并通过重载决议选择最佳者。转换的结果,若使用了转换构造函数,则为 cv 无限定版本 T右值临时量(直到 C++11)纯右值临时量(自 C++11)(直到 C++17)纯右值表达式(自 C++17),然后用于直接初始化对象。最后一步通常被优化掉,且转换结果被直接构造到为目标对象分配的内存中,但仍要求可访问适当的构造函数(移动或复制),即使它未被使用。(直到 C++17)
  • 否则(若 Tother 的类型都不是类类型),则必要时使用标准转换,以将 other 的值转换为 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

other 是右值表达式,则将通过重载决议选择移动构造函数并在复制初始化期间调用它。这仍被认为是复制初始化;此情况下没有特殊术语(例如,移动初始化)。

隐式转换是按复制初始化定义的:若能用表达式 E 复制初始化类型 T 的对象,则 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 期间创建的临时量的求值类别
类对象的复制初始化是未指明的
指明为右值

[编辑] 参见