103

C++11 では、クラスを初期化するための新しい構文があり、変数を初期化する方法が多数あります。

{ // Example 1
  int b(1);
  int a{1};
  int c = 1;
  int d = {1};
}
{ // Example 2
  std::complex<double> b(3,4);
  std::complex<double> a{3,4};
  std::complex<double> c = {3,4};
  auto d = std::complex<double>(3,4);
  auto e = std::complex<double>{3,4};
}
{ // Example 3
  std::string a(3,'x');
  std::string b{3,'x'}; // oops
}
{ // Example 4
  std::function<int(int,int)> a(std::plus<int>());
  std::function<int(int,int)> b{std::plus<int>()};
}
{ // Example 5
  std::unique_ptr<int> a(new int(5));
  std::unique_ptr<int> b{new int(5)};
}
{ // Example 6
  std::locale::global(std::locale("")); // copied from 22.4.8.3
  std::locale::global(std::locale{""});
}
{ // Example 7
  std::default_random_engine a {}; // Stroustrup's FAQ
  std::default_random_engine b;
}
{ // Example 8
  duration<long> a = 5; // Stroustrup's FAQ too
  duration<long> b(5);
  duration<long> c {5};
}

宣言する変数ごとに、どの初期化構文を使用するかを考える必要があり、これによりコーディング速度が低下します。中括弧を導入する意図がなかったことは確かです。

テンプレート コードに関しては、構文を変更すると異なる意味になる可能性があるため、正しい方法を使用することが不可欠です。

どの構文を選択すべきかという普遍的なガイドラインがあるのだろうか。

4

3 に答える 3

68

以下が良いガイドラインになると思います。

  • 初期化する(単一の)値がオブジェクトの正確な値であることを意図している場合は、copy(=)初期化を使用します(エラーが発生した場合、明示的なコンストラクターを誤って呼び出すことは決してないため、通常、提供された値を解釈します)異なる)。コピーの初期化が利用できない場所では、ブレースの初期化に正しいセマンティクスがあるかどうかを確認し、正しい場合はそれを使用します。それ以外の場合は、括弧の初期化を使用します (それも利用できない場合は、とにかく運が悪いです)。

  • 初期化する値がオブジェクトに格納される値のリストである場合(ベクトル/配列の要素、または複素数の実数/虚数部分など)、使用可能な場合は中括弧の初期化を使用します。

  • 初期化する値が格納される値ではなく、オブジェクトの意図した値/状態を記述する場合は、括弧を使用します。例は、 のサイズ引数vectorまたは のファイル名引数ですfstream

于 2012-04-02T13:38:26.810 に答える
28

普遍的なガイドラインが存在することはないと確信しています。私のアプローチは、それを覚えて常に中括弧を使用することです

  1. 初期化子リストのコンストラクターは他のコンストラクターよりも優先されます
  2. すべての標準ライブラリ コンテナーと std::basic_string には、初期化リスト コンストラクターがあります。
  3. 中かっこの初期化では、縮小変換は許可されません。

したがって、丸括弧と中括弧は交換できません。しかし、それらがどこで異なるかを知っていると、ほとんどの場合、丸括弧の初期化で中括弧を使用できます (現在使用できないケースのいくつかは、コンパイラのバグです)。

于 2012-04-02T13:20:18.597 に答える
17

一般的なコード (つまり、テンプレート) の外では、どこでも中かっこを使用できます (私も使用しています)。利点の 1 つは、クラス内の初期化など、どこでも機能することです。

struct foo {
    // Ok
    std::string a = { "foo" };

    // Also ok
    std::string b { "bar" };

    // Not possible
    std::string c("qux");

    // For completeness this is possible
    std::string d = "baz";
};

または関数の引数の場合:

void foo(std::pair<int, double*>);
foo({ 42, nullptr });
// Not possible with parentheses without spelling out the type:
foo(std::pair<int, double*>(42, nullptr));

T t = { init };またはスタイル間であまり注意を払っていない変数についてはT t { init };、違いはわずかであり、最悪の場合、explicitコンストラクターの誤用に関する有益なコンパイラ メッセージが表示されるだけです。

std::initializer_list明らかに非コンストラクターが必要な場合もありますが、受け入れる型のstd::initializer_list場合 (古典的な例はstd::vector<int> twenty_answers(20, 42);)。その場合、ブレースを使用しなくても問題ありません。


一般的なコード (つまり、テンプレート内) に関しては、最後の段落でいくつかの警告が発生するはずです。次の点を考慮してください。

template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{ return std::unique_ptr<T> { new T { std::forward<Args>(args)... } }; }

次に、 isauto p = make_unique<std::vector<T>>(20, T {});の場合はサイズ 2 のベクトルを作成し、 is の場合はサイズ 20 のベクトルを作成します。ここで何か非常に悪いことが起こっていることを示す非常に明白な兆候は、ここであなたを救うことができる特性がないことです (例えば、SFINAE で):干渉するコンストラクターがない場合にのみ初期化します。同様に役に立ちません。TintTstd::stringstd::is_constructiblestd::initializer_liststd::is_convertible

それを修正できる特性を手でロールすることが実際に可能かどうかを調査しましたが、それについてはあまり楽観的ではありません. make_unique<T>(foo, bar)いずれにせよ、多くのことを見逃しているとは思いませんT(foo, bar)。特に、とが同じタイプmake_unique<T>({ foo, bar })である場合にのみ意味があり、非常に似ていないことを考えると。foobar

したがって、一般的なコードでは、値の初期化(または など) に中括弧のみを使用します。これは非常に便利で、C++03 の方法よりも優れていると思います。それ以外の場合は、直接の初期化構文(つまり)、またはデフォルトの構築(私がそれを使用する唯一のケースだと思います)のいずれかです。T t {};T t = {};T t = T();T t(a0, a1, a2);T t; stream >> t;

ただし、すべての中かっこが悪いというわけではありません。前の例を修正して考えてみてください。

template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{ return std::unique_ptr<T> { new T(std::forward<Args>(args)...) }; }

std::unique_ptr<T>実際の型はテンプレート パラメーターに依存しますが、これは引き続き を構築するために中括弧を使用しますT

于 2012-04-02T15:30:35.750 に答える