27

ここ数日、C++11 をいじっていて、奇妙なことを思いつきました。

int を均一に初期化したい場合:

int a{5};

しかし、std::vector に対して同じことを行うと、次のようになります。

std::vector<int> b{2};

2 要素の配列を構築するのではなく、値 2 の 1 つの要素を持つ配列を構築します。それについてもっと明確にする必要があるという効果が得られるようです:

std::vector<int> c{{2}};
std::vector<int> d = {2};

しかし、b の宣言とは異なります。これは矛盾しているようです。私は同じ効果を持つ他のものを見てきました。私が尋ねているのは、この動作は最終的な C++11 標準での動作ですか、それとも初期に実装されたドラフトだけでしょうか? もしそうなら、なぜ標準化委員会はこの動作を含めたのですか? どのクラスがイニシャライザ リスト コンストラクタを持っているかを覚えておく必要があり、それらのクラスだけで {} の代わりに古い () 構文を使用する必要があるため、均一な初期化の目的全体が無効になっているようです。または、均一な初期化を完全に放棄します。

これは大きな「落とし穴」のようです。しかし、私が気づいていない利点があるかもしれません。

編集:このコード:

#include <iostream>
#include <vector>

int main() {
    std::vector<int> a{2};
    for (auto x: a) {
        std::cout << x << std::endl;
    }
    return 0;
}

gcc 4.6.2 で「2」を出力する

4

3 に答える 3

25

はい、§13.3.1.7 Initialization by list-initialization に従って、この動作は意図されています。

非集約クラス型 T のオブジェクトがリスト初期化される (8.5.4) 場合、オーバーロードの解決は 2 つのフェーズでコンストラクターを選択します。

— 最初は、候補関数はクラスの初期化子リスト コンストラクター (8.5.4) でTあり、引数リストは単一の引数としての初期化子リストで構成されます。

— 実行可能な初期化子リスト コンストラクターが見つからない場合、オーバーロードの解決が再度実行されます。この場合、候補関数はクラスのすべてのコンストラクターでTあり、引数リストは初期化子リストの要素で構成されます。

「均一な初期化の全体的な目的」については... 「均一な初期化」はマーケティング用語であり、あまり適切な説明ではありません。標準には、初期化とリスト初期化のすべての通常の形式がありますが、「均一な初期化」はありません。リストの初期化は、初期化の究極の形を意図したものではなく、ユーティリティ ベルトのもう 1 つのツールです。

于 2012-03-15T15:56:57.400 に答える
6

均一な初期化は、あなたが思っていることを意味するものではありません。C++ の型間で初期化をより均一にするために追加されました。理由は次のとおりです。

typedef struct dog_ {
   float height;
   int weight;
} dog;
int main() { 
    dog Spot = { 25.6, 45};
    dog Data[3] = { Spot, {6.5, 7} };
    std::array<dog, 2> data = { { Spot, {6.5, 7} } }; //only in C++ obviously
    return 0;
}

これは有効な C および C++ コードであり、何年も前から使用されています。これは非常に便利でしたが、これは POD タイプでしか機能しないことを覚えておく必要がありました。人々は長い間、 を行う方法がないと不満を漏らしてきましたがstd::vector<int> data = { 3, 7, 4, 1, 8};、一部のクラス ( std::array) は、イニシャライザ リスト コンストラクタを許可するために奇妙な方法で記述されていました。

そのため、C++11 では、委員会は vector や他のクールなクラスでもこれを実行できるようにしました。これにより、すべての型の構築がより均一になり、{}コンストラクターを介した初期化や、値リストからの初期化に使用できるようになりました。あなたが直面している問題は、コンストラクターのオーバーロードが anstd::initializer_list<int>で最もよく一致し、最初に選択されることです。そのため、std::vector<int> b{2};を受け取るコンストラクターを呼び出すことを意味するのではなく、この値のリストからintを作成することを意味します。その観点から、単一の値を含む を作成することは完全に理にかなっています。別のコンストラクターを呼び出すには、リストから初期化したくないことを C++ が認識できるように、構文を使用する必要があります。vectorintvector2()

于 2012-03-15T16:47:37.560 に答える
2

標準では、イニシャライザ リスト コンストラクタが他のコンストラクタよりも優先されると規定されています。これは、単に に置き換えることができない 1 つのケースにすぎませ(){}。他にもあります。たとえば、{}初期化では縮小変換は許可されません。

于 2012-03-15T15:54:12.890 に答える