web-dev-qa-db-ja.com

Cライク、コンストラクタ、統一初期化の違いは何ですか?

私の知る限り、C++で変数を初期化する方法は3つあります。

int x = 0;    // C-like initialization
int x (0);    // Constructor initialization
int x {0};    // Uniform initialization

C++ 11 の均一な初期化が導入され、 C +で異なる構文を必要とするさまざまなタイプの変数を初期化するためのより均一な構文が提供されました+03

Cライク、コンストラクタ、統一初期化の違いは何ですか?また、常に統一された初期化を使用する必要がありますか?

52
dayuloli

まず、ハーブサッターの 次の講演 をご覧になることをお勧めします。ブレース初期化の議論は 23:00頃 から始まります。

プリミティブデータ型の場合、3つすべてで同じ結果が得られます。私は個人的に古いint x = 0構文を使用することを好みますが、それは個人的な好みに帰着します。

クラス型の場合、ブレースの初期化と古いコンストラクターの初期化は完全に交換可能ではありません。例えば:

vector<int> v (100); // Creates a 100-element vector
vector<int> v {100}; // Creates a 1-element vector, holding the value 100.

これは、std::vectorに、std::initializer_listを唯一の引数として明示的に定義するコンストラクターがあるためです。それを念頭に置いて

auto var = {1, 2};

varを識別子としてstd::initializer_listを作成します。

イニシャライザリストの重要な点は、それらが一貫性を提供するということです。これは、事前に利用可能であったものからの歓迎すべき変更です。たとえば、C++で配列を初期化する場合は、次を使用します。

int arr[] = {1, 2, 3, 4};

ただし、vector<int>を同じ要素で初期化する場合は、次のいずれかを行う必要があります。

  1. 上記のarrを最初に初期化してから、arrarr + 4を渡します
  2. ベクトルを作成し、要素を個別にまたはループでPush_back()します。

C++ 11では、次のように使用できます

vector<int> v = {1, 2, 3, 4}; // Same syntax. Nice! Note that the = is optional

ブレースの初期化が役立つもう1つの例は、C++の 最も厄介な解析 の回避策を提供することです。話から、2つのクラスOriginextentsがあり、それらのインスタンスを渡してrectangle型の別のオブジェクトを構築できると仮定します。次のステートメント:

rectangle w(Origin(), extents());

rectangleおよびOrigin temporariesを使用してextentsオブジェクトを作成することはできません。そのステートメントは関数宣言として解析されるためです。 Tsk tsk。したがって、通常は次のようにする必要があります。

Origin  o;
extents e;
rectangle w(o, e);

ブレースの初期化を使用すると、その場で作成できます。

rectangle w {Origin(), extents()};

意図したとおりに機能します。つまり、最初の引数がOriginオブジェクトで、2番目の引数がextentsオブジェクトでオーバーロードされているコンストラクタに渡されます。

ルールはオブジェクト用です。特別な理由がない限り、ブレースの初期化を使用してください。

59
nasser-sh

Cライク、コンストラクター、および統一初期化の違いは何ですか?

intのようなプリミティブ型の場合、実用的な違いはありません。代わりに、クラス型Tを考えてみましょう。

最初のスタイルは次と同等です

T x(T(0));

初期化式から一時オブジェクトを作成し、それを移動またはコピーしてxを初期化します。実際には、移動またはコピーは省略されるため、結果は2番目のスタイルと同じになります。唯一の違いは、アクセス可能なコピーまたは移動コンストラクターがない場合、最初は失敗することです。

2番目は、1つの引数を取るコンストラクターを使用してオブジェクトを直接初期化し、適切なコンストラクターがない場合はエラーを出します。

3番目は、使用可能なコンストラクターによって異なります。

  • std::initializer_listを取るコンストラクターがある場合は、それを使用します。
  • それ以外の場合、適切な型の単一の引数を取るコンストラクターがある場合は、それを使用します。
  • それ以外の場合、1つのメンバーを持つ(コンストラクターがない)集合体の場合、そのメンバーはゼロで初期化されます。
  • それ以外の場合はエラーです。

また、常に初期化を使用する必要がありますか?

いいえ。場合によっては、initializer_listコンストラクタと他の引数タイプを取るコンストラクタを区別するために、関数スタイルの初期化が必要になることがあります。例えば:

std::vector<int> v1(10, 42);  // 10 elements with value 42
std::vector<int> v2{10, 42};  // 2 elements with values 10 and 42

また、意味のある意味で「均一」ではないため、「均一初期化」と呼ばないでください。正式な用語は「ブレース初期化」です。

15
Mike Seymour