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


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

どうして?

SOで答えが見つからなかったので、自分の質問に答えましょう。


12
なぜ使用しないのautoですか?
マークガルシア

33
それは本当です、それは便利ですが、それは私の意見では読みやすさを減らします-私はコードを読むときにオブジェクトがどんなタイプであるかを見たいです。オブジェクトのタイプが100%確実である場合、なぜautoを使用するのですか?また、リストの初期化を使用する場合(私の答えを読んでください)、それが常に正しいことを確認できます。
Oleksiy 2013

102
@Oleksiy:std::map<std::string, std::vector<std::string>>::const_iteratorあなたと一言お願いします。
Xeo 2013

9
@Oleksiy このGotWを読むことをお勧めします。
Rapptz 2013

17
@doc私using MyContainer = std::map<std::string, std::vector<std::string>>;はもっといいと思います(特にテンプレートできるので!)
JAB

回答:


357

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

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

  • 整数を、その値を保持できない別の整数に変換することはできません。たとえば、char to intは許可されていますが、int to 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)

}

のみ使用する場合=が好まれる状況は、{}でauto初期化することによって決定タイプを取得するキーワード。

例:

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

結論

特別な理由がない限り、代替手段よりも{}初期化を優先してください。


52
また、usingを()関数宣言として解析できることもあります。それは混乱し、あなたが言うことができるという矛盾しているT t(x,y,z);ではありませんT t()。そして時々、あなたは確かにx、あなたは言うことすらできませんT t(x);
juanchopanza 2013

84
私はこの答えに強く反対します。Actorがを受け入れるタイプがある場合、ブレース初期化は完全に混乱しますstd::initializer_list。RedXIIIはこの問題に言及しています(そしてそれを一掃します)が、あなたはそれを完全に無視します。A(5,4)そして、A{5,4}完全に異なる機能を呼び出し、これは知るべき重要なことであることができます。直感的に見えない呼び出しが発生することもあります。あなたが{}デフォルトで好むべきであると言うことは何が起こっているのか人々に誤解をもたらすでしょう。しかし、これはあなたのせいではありません。個人的には、これは非常によく考えられていない機能だと思います。
user1520427 2015

13
@ user1520427だからこそ、「しないという強い理由がない限り」という部分があります。
Oleksiy、2015

67
この質問は古くなっていますが、かなりのヒット数があるため、参考のためにここに追加します(ページの他の場所では見ていません)。C ++ 14の新しいとからブレース-のinit-リストから自動控除のためのルール、書くことができるようになりましたauto var{ 5 }、それはと推測されますintこれ以上のようstd::initializer_list<int>
Edoardo Sparkon Dominici 2015

13
ハハ、すべてのコメントから、それはまだ何をすべきか明確ではありません。明らかなのは、C ++仕様が混乱していることです。
DrumM

113

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

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

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

たとえば、次のような標準のライブラリタイプとうまく適合しますstd::vector

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

vector<int> b(10,20);   //Parentheses -> 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

11
あなたの答えのほとんどに完全に同意します。ただし、vectorに空のブレースを配置することは冗長であると思いませんか?つまり、ジェネリック型Tのオブジェクトを値初期化する必要がある場合は問題ありませんが、非ジェネリックコードに対してそれを行う目的は何ですか?
ミハイル、

8
@Mikhail:確かに冗長ですが、ローカル変数の初期化を常に明示的にするのは私の習慣です。私が書いたように、これは主に一貫性に関するものなので、重要な場合は忘れないでください。それは確かに私がコードレビューで言及したり、スタイルガイドに入れたりするものではありません。
MikeMB 2016年

4
かなりクリーンなルールセット。
laike9m

5
これは断然最良の答えです。{}は継承のようなものです。悪用されやすく、コードを理解するのが難しくなります。
UKMonkey 2018年

2
@MikeMBの例:const int &b{}<-初期化されていない参照を作成しようとせず、一時的な整数オブジェクトにバインドします。2番目の例:struct A { const int &b; A():b{} {} };<-は、初期化されていない参照の作成を試みません(()そうするように)が、それを一時的な整数オブジェクトにバインドし、ぶら下げたままにします。GCC -Wallは2番目の例を警告しません。
ヨハネスシャウブ-litb

91

ブレースの初期化を使用する多くの理由がありますが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!!!
}

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


20
これは、一般的なプログラミングにおいて非常に重要なポイントです。テンプレートを書くとき、ありません(のための標準の名前ブレース-INIT-リストを使用{ ... }したい場合を除き)initializer_listのセマンティクス(まあ、多分デフォルト・構築物のために)。
Xeo 2013

82
正直なところ、なぜstd::initializer_listルールが存在するのか理解できません。それは、混乱と混乱を言語に追加するだけです。コンストラクタFoo{{a}}が必要な場合の何が問題になっていますstd::initializer_listか?これは、std::initializer_list他のすべてのオーバーロードよりも優先されるよりもはるかに理解しやすいようです。
user1520427

5
上記のコメントの+1、それは本当に混乱していると思うので!! それは論理ではありません。Foo{{a}}私にとっていくつかのロジックに従いますが、それよりもFoo{a}初期化リストの優先順位が高くなります(ユーザーはhm ...と思うかもしれません)
Gabriel

32
基本的にC ++ 11は1つの混乱を別の混乱に置き換えます。ああ、申し訳ありませんが、置き換えられません-追加されます。そのようなクラスに遭遇していない場合、どうすればわかりますか?コンストラクターなしで 開始したが、インターフェイスを拡張するためにある時点でクラスに追加std::initializer_list<Foo>れる場合はどうでしょうか。次に、クラスのユーザーが台無しにされます。FooFoo
DOC

10
..「ブレース初期化を使用する多くの理由」とは何ですか?この回答は1つの理由(initializer_list<>)を指摘していますが、これは、がそれが好ましいと言っているのかを実際に限定するものはありません。他に30人(2016年4月21日現在)が役に立ったとは何ですか?
dwanderson

0

GoogleがChromiumで行うように-Wno-narrowingで構築しない限り、より安全です。そうした場合、安全性は低下します。ただし、このフラグがなければ、安全でないケースはC ++ 20で修正されるだけです。

注:A)中括弧は狭めることができないため、より安全です。B)中括弧は、プライベートまたは削除されたコンストラクターをバイパスし、明示的にマークされたコンストラクターを暗黙的に呼び出すことができるため、安全性が低くなります。

これらの2つの組み合わせは、内部がプリミティブ定数の場合は安全であることを意味しますが、オブジェクトの場合は安全ではありません(C ++ 20では修正されています)。


提供されたサンプルコードを使用して「explicit」または「private」コンストラクタをバイパスし、いずれか一方をプライベートまたは明示的にするようにgoldbolt.orgを試してみたところ、適切なコンパイラエラーが発生しました。いくつかのサンプルコードでバックアップしますか?
Mark Storer

これは、C ++ 20で提案された問題の修正です。open
Allan Jensen

1
回答を編集して、話しているC ++のバージョンを表示する場合は、投票を変更させていただきます。
Mark Storer
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.