名稱空間
變體
操作

複製初始化

來自 cppreference.com
< cpp‎ | 語言
 
 
C++ 語言
 
 

用另一個物件初始化一個物件。

目錄

[編輯] 語法

T 物件 = 其他; (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 是一個類型別,並且初始化器是一個 prvalue 表示式,其 cv-unqualified 型別與 T 相同,則使用初始化器表示式本身(而不是從其具現化的臨時物件)來初始化目標物件:參見複製消除
(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 在類物件複製初始化期間建立的臨時物件的
值類別未指定
指定為右值

[編輯] 另請參見