リスト初期化 (C++11以上)
波括弧初期化子リストからオブジェクトを初期化します。
目次 |
[編集]構文
[編集]直接リスト初期化
Tobject{ arg1, arg2, ...}; | (1) | ||||||||
T{ arg1, arg2, ...} | (2) | ||||||||
new T{ arg1, arg2, ...} | (3) | ||||||||
Class{ Tmember{ arg1, arg2, ...}; }; | (4) | ||||||||
Class:: Class() : member{ arg1, arg2, ...} {... | (5) | ||||||||
[編集]コピーリスト初期化
Tobject= { arg1, arg2, ...}; | (6) | ||||||||
function( { arg1, arg2, ...} ) | (7) | ||||||||
return { arg1, arg2, ...} ; | (8) | ||||||||
object[ { arg1, arg2, ...} ] | (9) | ||||||||
object= { arg1, arg2, ...} | (10) | ||||||||
U( { arg1, arg2, ...} ) | (11) | ||||||||
Class{ Tmember= { arg1, arg2, ...}; }; | (12) | ||||||||
リスト初期化は以下の状況で行われます。
- 直接リスト初期化 (explicit なコンストラクタと非 explicit なコンストラクタの両方が考慮されます)
- コピーリスト初期化 (explicit なコンストラクタと非 explicit なコンストラクタの両方が考慮されますが、非 explicit なコンストラクタだけが呼ばれる可能性があります)
[編集]説明
T
型のオブジェクトのリスト初期化の効果は以下の通りです。
| (C++14以上) |
| (C++14未満) |
| (C++14以上) |
- そうでなく、
T
が std::initializer_list の特殊化の場合、T
のオブジェクトは、波括弧初期化子リストから初期化された同じ型の prvalue (C++17未満)から、文脈に応じて直接初期化またはコピー初期化されます。
- そうでなければ、以下の2つのフェーズで
T
のコンストラクタが考慮されます。
- 唯一の引数として (または残りの引数がデフォルト値を持つ場合の最初の引数として) std::initializer_list を取るすべてのコンストラクタが調べられ、 std::initializer_list 型の引数1個に対するオーバーロード解決によってマッチされます。
- 前のステージでマッチがなかった場合、
T
のすべてのコンストラクタが波括弧初期化子リストの要素によって構成される引数の集合に対するオーバーロード解決に参加します。 ただし縮小変換は許されないという制限が付きます。 このステージでコピーリスト初期化に対するベストマッチとして explicit コンストラクタが選択された場合、コンパイルは失敗します (単純なコピー初期化では explicit コンストラクタは一切考慮されないことに注意してください)。
- 前のステージでマッチがなかった場合、
| (C++17以上) |
- そうでなく (
T
がクラス型でない)、波括弧初期化子リストが要素を1個だけ持ち、T
が参照型でないか、T
が参照型でその参照先の型がその要素の型と同じまたは基底クラスであるか、のいずれかの場合、T
は直接初期化 (直接リスト初期化の場合) またはコピー初期化 (コピーリスト初期化の場合) されます。 ただし縮小変換は許されません。
- そうでなく、
T
がその要素の型と互換でない場合、その参照先の型の一時オブジェクトがリスト初期化され、その一時オブジェクトに参照が束縛されます (参照が非 const 左辺値参照の場合、これは失敗します)。
- そうでなく、波括弧初期化子リストが要素を持たない場合、
T
は値初期化されます。
[編集]縮小変換
リスト初期化は許される暗黙の変換を制限します。 以下の内容が禁止されます。
- 浮動小数点型から整数型への変換。
- longdouble から double または float への変換および double から float への変換。 ただし変換元が定数式でオーバーフローが発生しない場合は除きます。
- 整数型から浮動小数点型への変換。 ただし変換元が定数式で、その値が変換先の型に正確に格納できる場合は除きます。
- 整数型またはスコープなし列挙型から、元の型のすべての値を表現できない整数型への変換。 ただし変換元が定数式で、その値が変換先の型に正確に格納できる場合は除きます。
| (C++20以上) |
[編集]ノート
すべての初期化子節は、波括弧初期化子リスト内でそれより後にあるあらゆる初期化子節に対して先行配列されます。 これは、配列されない関数呼び出し式の引数とは対照的です
波括弧初期化子リストは式ではなく、そのため型がありません。 例えば decltype({1,2}) は ill-formed です。 型がないということは、テンプレート型推定が波括弧初期化子リストにマッチする型を推定できないということを意味し、そのため宣言 template<class T>void f(T); が与えられたとき、式 f({1,2,3}) は ill-formed です。 しかし、それ以外はテンプレート引数を推定でき、例えば std::vector<int> v(std::istream_iterator<int>(std::cin), {}) の場合、イテレータの型は第1引数によって推定され、それが第2引数の位置でも使用されます。 キーワード auto を用いた型推定については特別な例外があり、コピーリスト初期化では、あらゆる波括弧初期化子リストは std::initializer_list として推定されます。
また、波括弧初期化子リストには型がないため、オーバーロードされた関数呼び出しへの引数として使用されたときは、オーバーロード解決のための特別なルールが適用されます。
集成体のコピー/ムーブは同じ型の単一要素の波括弧初期化子リストから直接的に初期化しますが、非集成体は initializer_list コンストラクタを最初に考慮します。 struct X { X()=default; X(const X&)=default;}; struct Q { Q()=default; Q(Q const&)=default; Q(std::initializer_list<Q>){}}; int main(){ X x; X x2 = X { x };// コピーコンストラクタ (集成体初期化ではない) Q q; Q q2 = Q { q };// 初期化子リストコンストラクタ (コピーコンストラクタではない)} | (C++14以上) |
[編集]例
#include <iostream>#include <vector>#include <map>#include <string> struct Foo {std::vector<int> mem ={1,2,3};// 非静的メンバのリスト初期化std::vector<int> mem2; Foo(): mem2{-1, -2, -3}{}// コンストラクタでのメンバのリスト初期化}; std::pair<std::string, std::string> f(std::pair<std::string, std::string> p){return{p.second, p.first};// return 文でのリスト初期化} int main(){int n0{};// (ゼロへの) 値初期化int n1{1};// 直接リスト初期化std::string s1{'a', 'b', 'c', 'd'};// 初期化子リストコンストラクタの呼び出しstd::string s2{s1, 2, 2};// 普通のコンストラクタの呼び出しstd::string s3{0x61, 'a'};// 初期化子リストコンストラクタが (int, char) よりも優先されます int n2 ={1};// コピーリスト初期化double d =double{1.2};// 一時オブジェクトのリスト初期化、その後コピー初期化 std::map<int, std::string> m ={// ネストしたリスト初期化{1, "a"}, {2, {'a', 'b', 'c'}}, {3, s1}}; std::cout<< f({"hello", "world"}).first// 関数呼び出しでのリスト初期化<<'\n'; constint(&ar)[2]={1,2};// 左辺値参照を一時配列に束縛しますint&& r1 ={1};// 右辺値参照を int の一時オブジェクトに束縛します// int& r2 = {2}; // エラー、右辺値を非 const 左辺値参照に束縛できません // int bad{1.0}; // エラー、縮小変換unsignedchar uc1{10};// OK// unsigned char uc2{-1}; // エラー、縮小変換 Foo f; std::cout<< n0 <<' '<< n1 <<' '<< n2 <<'\n'<< s1 <<' '<< s2 <<' '<< s3 <<'\n';for(auto p: m)std::cout<< p.first<<' '<< p.second<<'\n';for(auto n: f.mem)std::cout<< n <<' ';for(auto n: f.mem2)std::cout<< n <<' ';}
出力:
world 0 1 1 abcd cd aa 1 a 2 abc 3 abcd 1 2 3 -1 -2 -3
[編集]欠陥報告
以下の動作変更欠陥報告は以前に発行された C++ 標準に遡って適用されました。
DR | 適用先 | 発行時の動作 | 正しい動作 |
---|---|---|---|
CWG 1467 | C++14 | same-type initialization of aggregates and char arrays was prohibited | same-type initialization allowed |
CWG 1467 | C++14 | std::initializer_list constructors had priority over copy constructors for single-element lists | single-element lists initialize directly |