◐ Shell
clean mode source ↗

コピー初期化 - cppreference.com

提供: cppreference.com

オブジェクトを別のオブジェクトから初期化します。

構文

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}; (6)

説明

コピー初期化は以下の状況で行われます。

1) 非参照型 T の名前付き変数 (自動、静的、またはスレッドローカル) が等号とそれに続く式から構成される初期化子付きで宣言されたとき。

2) (C++11未満)スカラー型 T の名前付き変数が等号とそれに続く波括弧で囲まれた式から構成される初期化子付きで宣言されたとき (ノート: C++11 以降、これはリスト初期化に分類され、ナローイング変換は許容されません)。

4) 値で返す関数から戻るとき。

6) 集成体初期化の一部として、初期化子が提供されるそれぞれの要素を初期化するため。

コピー初期化の効果は以下の通りです。

  • まず、 T がクラス型であり、初期化子が prvalue であり、その prvalue の cv 修飾されていない型が T と同じクラスの場合、その初期化子の式自身 (それから具体化された一時オブジェクトではなく) が宛先のオブジェクトを初期化するために使用されます。 コピー省略を参照してください。
(C++17以上)
  • T がクラス型であり、 other の型の cv 修飾されていないバージョンが T または T から派生したクラスの場合、 T非 explicit コンストラクタが調べられ、オーバーロード解決によりベストマッチが選択されます。 そしてそのコンストラクタがそのオブジェクトを初期化するために呼ばれます。
  • T がクラス型であり、 other の型の cv 修飾されていないバージョンが T または T からの派生でない場合、または T は非クラス型だけれども other の型がクラス型の場合、 other の型から T (または T がクラス型で変換関数が利用可能な場合は T から派生した型) への変換が可能なユーザ定義変換シーケンスが調べられ、オーバーロード解決を通してベストなものが選択されます。 そして、その変換の結果 (変換コンストラクタが使用された場合、これは prvalue の一時オブジェクト (C++17未満)prvalue 式 (C++17以上)です) がそのオブジェクトを直接初期化するために使用されます。 この最後のステップは、通常、最適化によって除去され、変換の結果はターゲットオブジェクトのために確保されたメモリ内に直接構築されます。 しかし、たとえ使用されなくとも、適切なコンストラクタ (ムーブまたはコピー) は要求されます。 (C++17未満)
  • そうでなければ (Tother の型もクラス型でない場合)、 other の値を T の cv 修飾されていないバージョンに変換するために、必要であれば、標準変換が使用されます。

ノート

コピー初期化は直接初期化よりも非寛容です。 explicit コンストラクタ変換コンストラクタでなく、コピー初期化に対しては考慮されません。

struct Exp { explicit Exp(const char*) {} }; // const char* から変換できない
Exp e1("abc");  // OK
Exp e2 = "abc"; // エラー、コピー初期化は explicit コンストラクタを考慮しません

struct Imp { Imp(const char*) {} }; // const char* から変換可能
Imp i1("abc");  // OK
Imp i2 = "abc"; // OK

さらに、コピー初期化中の暗黙の変換は初期化子から直接 T を生成しなければならないのに対し、例えば直接初期化は初期化子から T のコンストラクタの引数への暗黙の変換を期待します。

struct S { S(std::string) {} }; // std::string から暗黙に変換可能
S s("abc"); // OK、 const char[4] から std::string への変換
S s = "abc"; // エラー、 const char[4] から S への変換はありません
S s = "abc"s; // OK、 std::string から S への変換

other が右辺値式の場合は、ムーブコンストラクタがオーバーロード解決によって選択され、コピー初期化中に呼ばれます。 ムーブ初期化とかいうような用語はありません。

暗黙の変換はコピー初期化によって定義されます。 T 型のオブジェクトが式 E を用いてコピー初期化可能な場合、 ET に暗黙に変換可能です。

名前付き変数のコピー初期化における等号 = は代入演算子と無関係です。 代入演算子のオーバーロードはコピー初期化には影響しません。

#include <string>
#include <utility>
#include <memory>
 
struct A 
{
  operator int() { return 12;}
};
 
struct B 
{
  B(int) {}
};
 
int main()
{
    std::string s = "test"; // OK、コンストラクタは explicit ではありません
    std::string s2 = std::move(s); // このコピー初期化はムーブを行います
 
//  std::unique_ptr<int> p = new int(1); // エラー、コンストラクタが explicit です
    std::unique_ptr<int> p(new int(1)); // OK、直接初期化
 
 
    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; // エラー、代入演算子のオーバーロードが必要です
}

関連項目