Copy-initialization
別のオブジェクトからオブジェクトを初期化します。
目次 |
構文
T
オブジェクト
=
他のオブジェクト
;
|
(1) | ||||||||
T
オブジェクト
=
{
他のオブジェクト
};
|
(2) | (C++11以前) | |||||||
関数f
(
他のオブジェクト
)
|
(3) | ||||||||
return
他のオブジェクト
;
|
(4) | ||||||||
throw
オブジェクト
;
|
(5) | ||||||||
T
配列
[
N
] = {
他のシーケンス
};
|
(6) | ||||||||
説明
コピー初期化は以下の状況で実行されます:
T
の名前付き変数(自動、静的、またはスレッドローカル)が、等号とそれに続く式からなる初期化子で宣言された場合。
T
の名前付き変数が、等号とそれに続く波括弧で囲まれた式からなる初期化子で宣言される場合(注: C++11以降、これは
リスト初期化
として分類され、縮小変換は許可されません)。
コピー初期化の効果は以下の通りです:
|
(C++17以降) |
-
最初に
(C++17まで)
それ以外の場合
(C++17以降)
、
Tがクラス型であり、 other の型のCV修飾されていないバージョンがTまたはTから派生したクラスである場合、Tの 非明示的コンストラクタ が調べられ、オーバーロード解決によって最適な一致が選択されます。その後、そのコンストラクタがオブジェクトの初期化のために呼び出されます。
-
それ以外の場合、
Tがクラス型であり、かつ other の型のCV修飾なしバージョンがTまたはTから派生した型でない場合、またはTが非クラス型であるが other の型がクラス型である場合、 ユーザー定義変換シーケンス が調査され、 other の型からT(またはTがクラス型で変換関数が利用可能な場合はTから派生した型)へ変換可能な最適なものがオーバーロード解決を通じて選択される。変換の結果は、 右値一時オブジェクト (C++11まで) 純粋右値一時オブジェクト (C++11から) (C++17まで) 純粋右値式 (C++17から) であり、 変換コンストラクタ が使用された場合はTのCV修飾なしバージョンの一時オブジェクトが生成され、その後 直接初期化 によってオブジェクトが初期化される。 最終ステップは通常 最適化により省略 され、変換結果は対象オブジェクト用に確保されたメモリに直接構築されるが、適切なコンストラクタ(ムーブまたはコピー)は使用されない場合でもアクセス可能である必要がある。 (C++17まで)
-
それ以外の場合(
Tと other の型のいずれもクラス型でない場合)、必要に応じて 標準変換 を使用して、 other の値をTのCV修飾なしバージョンに変換します。
注記
コピー初期化は直接初期化よりも制限が厳しい: explicitコンストラクタ は convertingコンストラクタ ではなく、コピー初期化では考慮されない。
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 が右辺値式である場合、 move constructor がオーバーロード解決によって選択され、コピー初期化中に呼び出されます。これは依然としてコピー初期化と見なされます。このケースに対する特別な用語(例:ムーブ初期化)はありません。
暗黙の変換
はコピー初期化の観点から定義されます: 型
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: コンストラクタは非explicit std::string s2 = std::move(s); // このコピー初期化はmoveを実行 // 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; // < エラー、代入演算子のオーバーロードが必要 [](...){}(c, b0, b3, b4); // これらの変数が使用されていることを仮定 }
不具合報告
以下の動作変更の欠陥報告書は、以前に公開されたC++規格に対して遡及的に適用されました。
| DR | 適用対象 | 公開時の動作 | 正しい動作 |
|---|---|---|---|
| CWG 5 | C++98 |
変換コンストラクタによって初期化される一時オブジェクトに
宛先型のcv修飾が適用される |
一時オブジェクトはcv修飾されない |
| CWG 177 | C++98 |
クラスオブジェクトのコピー初期化中に作成される一時オブジェクトの
値カテゴリが未規定 |
右辺値として規定 |