コピー初期化
From cppreference.com
あるオブジェクトから別のオブジェクトを初期化します。
目次 |
[編集] 構文
T object = other; |
(1) | ||||||||
T object = {other}; |
(2) | (C++11まで) | |||||||
f(other) |
(3) | ||||||||
return other; |
(4) | ||||||||
throw object;
|
(5) | ||||||||
T array[N] = {other-sequence}; |
(6) | ||||||||
[編集] 説明
コピー初期化は次の状況で行われます。
1) 非参照型
T の名前付き変数 (自動、静的、またはスレッドローカル) が、等号とその後に続く式からなる初期化子で宣言されている場合。2) (C++11まで) スカラ型
T の名前付き変数が、等号とその後に続くブレースで囲まれた式からなる初期化子で宣言されている場合 (注: C++11以降、これはリスト初期化として分類され、縮小変換は許可されません)。3) 値渡しで関数に引数を渡す場合。
4) 値を返す関数から戻る場合。
6) 集成体初期化の一部として、初期化子が提供されている各要素を初期化する場合。
コピー初期化の効果は次のとおりです。
| (C++17以降) |
- それ以外の場合、
Tがクラス型であり、other の型の cv-unqualified バージョンがTまたはTから派生したクラスである場合、Tの非明示的コンストラクタが調べられ、オーバーロード解決によって最適なものが選択されます。そのコンストラクタが呼び出されてオブジェクトが初期化されます。
- それ以外の場合、
Tがクラス型であり、other の型の cv-unqualified バージョンがTまたはTから派生したものではない場合、またはTが非クラス型であるが other の型がクラス型である場合、other の型からT(またはTがクラス型で変換関数が利用できる場合はTから派生した型) に変換できるユーザー定義変換シーケンスが調べられ、オーバーロード解決によって最適なものが選択されます。変換の結果、つまり、変換コンストラクタが使用された場合はTの cv-unqualified バージョンのrvalue一時オブジェクト(C++11まで)prvalue一時オブジェクト(C++11以降)(C++17まで)prvalue式(C++17以降)は、オブジェクトを直接初期化するために使用されます。最後のステップは通常最適化され、変換の結果はターゲットオブジェクトに割り当てられたメモリに直接構築されますが、適切なコンストラクタ (ムーブまたはコピー) は使用されない場合でもアクセス可能である必要があります。(C++17まで)
- それ以外の場合 (
Tも other の型もクラス型ではない場合)、必要に応じて、other の値をTの cv-unqualified バージョンに変換するために標準変換が使用されます。
[編集] 注記
コピー初期化は直接初期化よりも許容範囲が狭く、明示的コンストラクタは変換コンストラクタではなく、コピー初期化では考慮されません。
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 が rvalue 式である場合、オーバーロード解決によってムーブコンストラクタが選択され、コピー初期化中に呼び出されます。これは依然としてコピー初期化と見なされ、このケースには特別な用語 (例: ムーブ初期化) はありません。
暗黙的な変換はコピー初期化に関して定義されています。つまり、型 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 | クラスオブジェクトのコピー初期化中に作成された一時オブジェクトの 値カテゴリが未指定である |
rvalueとして指定される |