web-dev-qa-db-ja.com

リストの初期化(中括弧を使用)が他の選択肢よりも優れているのはなぜですか。

MyClass a1 {a};     // clearer and less error-prone than the other three
MyClass a2 = {a};
MyClass a3 = a;
MyClass a4(a);

どうしてですか?

SOで答えが見つからなかったので、私自身の質問に答えさせてください。

305
Oleksiy

基本的にBjarne Stroustrupの "The C++ Programming Language 4th Edition"からコピーして貼り付けます。

リストの初期化は絞り込みを許可しません(§iso.8.5.4)。あれは:

  • 整数をその値を保持できない別の整数に変換することはできません。たとえば、charからintへの変換は許可されますが、intからcharへの変換は許可されません。
  • 浮動小数点値を、その値を保持できない別の浮動小数点型に変換することはできません。たとえば、floatからdoubleへの変換は許可されていますが、doubleからfloatへの変換はできません。
  • 浮動小数点値は整数型に変換できません。
  • 整数値を浮動小数点型に変換することはできません。

例:

void fun(double val, int val2) {

    int x2 = val; // if val==7.9, x2 becomes 7 (bad)

    char c2 = val2; // if val2==1025, c2 becomes 1 (bad)

    int x3 {val}; // error: possible truncation (good)

    char c3 {val2}; // error: possible narrowing (good)

    char c4 {24}; // OK: 24 can be represented exactly as a char (good)

    char c5 {264}; // error (assuming 8-bit chars): 264 cannot be 
                   // represented as a char (good)

    int x4 {2.0}; // error: no double to int value conversion (good)

}

=が{}よりも優先されるonlyの状況は、初期化子によって決定された型を取得するためにautoキーワードを使用する場合です。

例:

auto z1 {99}; // z1 is an initializer_list<int>
auto z2 = 99; // z2 is an int

結論

強い理由がない限り、代わりに{}初期化を使用してください。

276
Oleksiy

中括弧の初期化を使用する理由はたくさんありますが、initializer_list<>コンストラクターが他のコンストラクターより優先されることに注意してください。例外はdefault-constructorです。 。これは、T型のコンストラクタが初期化子リストか単純な古いctorのどちらかになる可能性があるコンストラクタとテンプレートに問題を引き起こします。

struct Foo {
    Foo() {}

    Foo(std::initializer_list<Foo>) {
        std::cout << "initializer list" << std::endl;
    }

    Foo(const Foo&) {
        std::cout << "copy ctor" << std::endl;
    }
};

int main() {
    Foo a;
    Foo b(a); // copy ctor
    Foo c{a}; // copy ctor (init. list element) + initializer list!!!
}

そのようなクラスに遭遇しないと仮定すると、初期化リストを使用しない理由はほとんどありません。

83
Red XIII

リストの初期化を使用する利点についてはすでにすばらしい答えがありますが、私の個人的な経験則では可能な限り中括弧を使用するのではなく、概念上の意味に依存させるようにします。

  • 私が作成しているオブジェクトがコンストラクタで渡している値(たとえば、コンテナ、POD構造体、アトミック、スマートポインタなど)を概念的に保持している場合は、中括弧を使用します。
  • コンストラクタが通常の関数呼び出しに似ている(引数によってパラメータ化された多少複雑な操作を実行する)場合は、通常の関数呼び出し構文を使用します。
  • デフォルトの初期化では、私はいつも中括弧を使います。
    [1] 1つには、そのようにして、オブジェクトが初期化されるかどうかに関係なく、オブジェクトが初期化されることを常に確信しています。とにかく呼び出されるデフォルトのコンストラクタを持つ「本物の」クラス、または組み込み/ POD型です。次に、デフォルトの初期化済みオブジェクトは「空の」オブジェクトを表すことが多いため、ほとんどの場合、最初の規則と一致しています。

私の経験では、このルールセットはデフォルトで中括弧を使用するよりもずっと一貫して適用できますが、使用できない場合や、括弧付きの「通常の」関数呼び出し構文とは異なる意味を持つ場合はすべての例外を明示的に記憶する(別のオーバーロードを呼び出す).

例えばstd::vectorのような標準的なライブラリタイプにうまくフィットします:

vector<int> a{10,20};   //Curly braces -> fills the vector with the arguments

vector<int> b(10,20);   //Parenthesis -> uses arguments to parametrize some functionality,                          
vector<int> c(it1,it2); //like filling the vector with 10 integers or copying a range.

vector<int> d{};      //empty braces -> default constructs vector, which is equivalent
                      //to a vector that is filled with zero elements
76
MikeMB