Scott Meyers によるドラフト本「Effective C++11 」には、次のように記載されています。
オブジェクトの作成時に () と {} を区別する
Object obj(args...)
とはどう違いObject obj{args...}
ますか?なぜスコットがそう言うのか。
アップデート:
質問C++11 統一初期化構文の使用方法は? は HOW を尋ね、この質問は WHY を尋ねます。
更新 2:
次のリンクが役に立ち、この質問に完全に答えています。
Scott Meyers によるドラフト本「Effective C++11 」には、次のように記載されています。
オブジェクトの作成時に () と {} を区別する
Object obj(args...)
とはどう違いObject obj{args...}
ますか?なぜスコットがそう言うのか。
アップデート:
質問C++11 統一初期化構文の使用方法は? は HOW を尋ね、この質問は WHY を尋ねます。
更新 2:
次のリンクが役に立ち、この質問に完全に答えています。
Object obj(args...)
とはどう違いObject obj{args...}
ますか?
1 つ目はdirect-initializationで、2 つ目はdirect-list-initializationです。これは、次の 2 つのセクションで説明されています。
§8.5/16 [dcl.init]
フォームで発生する初期化
T x(a); T x{a};
new
式 (5.3.4)、static_cast
式 (5.2.9)、関数表記型変換 (5.2.3)、および基本およびメンバー初期化子 (12.6.2) と同様に、直接初期化と呼ばれます。
および§8.5.4/1 [dcl.init.list]
list -initializationは、braced-init-listからのオブジェクトまたは参照の初期化です。このような初期化子は初期化子リストと呼ばれ、リストのコンマ区切りの初期化句は初期化子リストの要素と呼ばれます。初期化リストは空である可能性があります。リスト初期化は、直接初期化コンテキストまたはコピー初期化コンテキストで発生する可能性があります。直接初期化コンテキストでのリスト初期化は直接リスト初期化と呼ばれ、コピー初期化コンテキストでのリスト初期化はコピーリスト初期化と呼ばれます。
この 2 つにはいくつかの違いがあります。
initializer_list
構築される型に引数を取るコンストラクターがある場合、直接リスト初期化は常にそのコンストラクターを優先します。他のコンストラクターは、コンストラクターが実行可能でない場合にのみ考慮されinitializer_list
ます。§13.3.1.7/1 [over.match.list]
direct-list-initializationでは、引数リスト内での縮小変換は許可されません。§8.5.4/3 [dcl.init.list]
初期化される型が集合体の場合、direct-list-initializationは集合体の初期化を実行します。§8.5.4/3 [dcl.init.list]
ブレース初期化リストの要素の評価順序は、左から右です。§8.5.4/4 [dcl.init.list]
direct-list-initializationを使用すると、最も厄介な解析を回避できます。
struct foo{};
struct bar
{
bar(foo const&) {}
};
bar b1(foo()); // most vexing parse
bar b2(foo{}); // all 3 of the following construct objects of type bar
bar b3{foo()};
bar b4{foo{}};
との動作はObject obj(args...)
、 でObject{args...}
定義されたコンストラクタに依存しObject
ます。
次の例を見てください。
#include <iostream>
#include <initializer_list>
struct A
{
A(int a, int b) {std::cout << "Came to A::A()\n";}
};
struct B
{
B(int a, int b) {std::cout << "Came to B::B(int, int)\n";}
B(std::initializer_list<int> in) {std::cout << "Came to B::B(std::initializer_list<int>)\n";}
};
int main()
{
A a1(10, 20); // Resolves to A(int, int)
A a2{10, 20}; // Resolves to A(int, int)
A a3{30}; // Does not resolve to anything. It's a compiler error.
B b1(10, 20); // Resolves to B(int, int)
B b2{10, 20}; // Resolves to B(std::initializer_list<int> )
B b3{30}; // Resolves to B(std::initializer_list<int> )
}
Object obj(args...) と Object obj{args...} の違いは何ですか?
{args...} は、他の正当な候補よりも initializer_list を持つコンストラクターを優先します。
std::vector<int> v(10); // vector of size 10
std::vector<int> v{10}; // vector initialized with a single element, (int) 10
一方、暗黙の狭小化は放棄します。
std::vector<int> v(10.5); // vector of size 10
std::vector<int> v{10.5}; // illegal - no compile
std::vector<float> v{10.5}; // vector initialized with a single element, (float) 10.5
Object obj(args...) と Object obj{args...} の違いは何ですか? なぜスコットがそう言うのか。
違いは、前者の場合、引数の評価順序は順不同 (つまり、指定されていない) ですが、後者の場合、順序は左から右 (つまり、それらの出現順) です。
Object(args...)
$5.2.2/8 [expr.call] (n3690) からの次のテキストは、フォームを扱っています。
後置式と引数の評価はすべて相互に順序付けされていません。引数評価のすべての副作用は、関数に入る前に順序付けされます (1.9 を参照)。
Object{args...}
$8.5.4/4 [dcl.init.list] (n3690) のテキストは、次の形式を扱っています。
ブレース初期化リストの初期化リスト内では、パック展開 (14.5.3) の結果を含む初期化節は、出現順に評価されます。つまり、指定されたinitializer-clauseに関連付けられたすべての値の計算と副作用は、initializer-listのコンマ区切りリストでそれに続くすべてのinitializer-clauseに関連付けられたすべての値の計算と副作用の前に順序付けられます。[注: この評価の順序は、初期化のセマンティクスに関係なく保持されます。たとえば、通常は呼び出しの引数に順序付けの制約がない場合でも、initializer-list の要素がコンストラクター呼び出しの引数として解釈される場合に適用されます。— エンドノート]
つまり、これは次のことを意味します。
int f() { static int i = 10; return ++i; } //increment the static int!
Object obj(f(), f()); //is it obj(11,12) or obj(12,11)? Unspecified.
Object obj{f(), f()}; //it is obj(11,12). Guaranteed.
GCC (4.7.0 および 4.7.2) にはバグがあり、{}
フォームが正常に機能しないことに注意してください。現在のバージョンで修正されているかどうかはわかりません。
それが役立つことを願っています。