17

多くの人と同じように、私はC++0xにかなり興奮しています。私は、新しいプロジェクトで新機能を学び、使用して、可能な限り最高の、最も保守しやすいコードを記述できるようにしています。

言うまでもなく、私は新しいイニシャライザーの背後にあるアイデアが大好きです。だから私はそれらを見ています、そしてこれらは私にとって理にかなっています:

T x = { 1, 2, 3 }; // like a struct or native array
T x({1, 2, 3});    // copy construct something like an "object literal" in other languages... cool!
return {1, 2, 3};  // similar to above, but returning it, even cooler!

私には意味がないのはこれです:

T x{1, 2, 3};

それはただ...奇妙に感じます。人々がどの構文を使用したいのか、これが模倣しているのかわかりません。単に「正しく」ないようです。

この構文の背後にある設計/考えは何ですか?

それが違いを生むように見える唯一の例は、次のようなものです。

std::vector<int> the_vec{4};

これは初期化リストコンストラクターを呼び出しますが、なぜこれを書いてみませんか?

std::vector<int> the_vec = {4};

そして、誰もがすでに快適なことをしますか?

4

4 に答える 4

26

この構文の背後にある設計/考え方は何ですか?

1 つには、ブレース構文を使用すると、面倒な解析を回避できます。

T x(); // function declaration
T x{}; // value-initialized object of type 'T' named 'x'

C++03 では、これに最も近いのはT x((T()));orT x = T();であり、どちらもTアクセス可能なコピー コンストラクターを必要とします。

于 2011-08-11T19:56:37.960 に答える
19

まず、実際には 1 つのものの 2 つのバリエーションがあります。

T x = { 1, 2, 3 };
T x{1, 2, 3};

これら 2 つは実際には同じ初期化を行っていますが、コンストラクターを選択した場合に最初のものが無効になるという例外がありますexplicit。それ以外は同一です。1 つ目は「コピー リスト初期化」と呼ばれ、2 つ目は「直接リスト初期化」と呼ばれます。

概念は、 を含むフォームが=「複合値」 (3 つの int で構成される値) を割り当てているということです。そしてx、その値で初期化します。このような初期化では、非 explicitコンストラクターのみを許可する必要があります。x{1, 2, 3}(等号なし)の概念は、変数を3 つの値で初期化することです。概念的には複合値ではなく、たまたま一度に 3 つの個別の値を指定することです。その用語の最も一般的な意味での「コンストラクター呼び出し」と言えます。

あなたが示した他の初期化は、実際には上記の2つとはまったく異なるものです:

T x({1, 2, 3});

Twithのコンストラクターを{1, 2, 3}引数として呼び出すだけです。が配列である場合の配列の初期化や、 が集約構造体/クラスであるT場合の構造体メンバーの初期化など、手の込んだことは一切行いません。がクラスでないT場合、その宣言は無効です。Tしかし、Tたまたまコピー コンストラクターまたはムーブ コンストラクターがある場合は、そのコンストラクターを使用してT、コピー リストの初期化によって一時的なコンストラクターを構築し、コピー/ムーブ コンストラクター参照パラメーターをその一時的なパラメーターにバインドできます。実際のコードでは、その形式はあまり必要ないと思います。


これはすべて、イニシャライザ リストの委員会提案書に記録されています。この場合、Initializer Lists — Alternative Mechanism and Rationaleのセクション「プログラマーの初期化の種類」を参照してください。

コピー初期化と直接初期化の違いを認識している熟練したプログラマーが、前者は後者よりも効率が悪いと誤って考えることがよくあります。(実際には、両方の初期化が理にかなっている場合、どちらも同じように効率的です。)

対照的に、これらのことを異なる用語で考える方がより有用であることがわかります。

  • コンストラクターの呼び出しによる構築 (「ctor-call」)
  • 値を転送して構築する (「変換」)

(たまたま、前者は「直接初期化」に対応し、後者は「コピー初期化」に対応しますが、標準の用語はプログラマーの役に立ちません。)

後で、彼らは見つけます

{ ... }inを扱うので注意してください。

X x = { ... };

単一の値として、それはと同等ではありません

X x{ ... };

ここで、{ ... }はコンストラクター呼び出しの引数リストです (N2531 とは異なるため強調します)。

C++0x FDIS に記載されている規則は、その文書に示されているものとは少し異なりますが、その文書に示されている理論的根拠は C++0x FDIS に保持され、実装されています。

于 2011-08-11T20:15:53.187 に答える
4

与えられた答えは理論的な観点からは素晴らしいですが、おそらくいくつかの実用的な例も役立つでしょう. 均一な初期化により、以前は単純に不可能だった構造を記述できるようになりました。例えば:

  • メンバー配列を初期化します。

  • グローバル定数コンテナー (マップなど)。

ウィット:

class Foo
{
  int data[3];
public:
  Foo() : data{1,2,3} { }
};

ここでは、代入を必要とせずにメンバー配列を直接初期化できます (デフォルトの構築が利用できない状況を考慮してください)。

const std::map<int, std::string> labels {
  { 1 , "Open" },
  { 2 , "Close" },
  { 3 , "Reboot" } };

読み取り専用のグローバル ルックアップ オブジェクトが役立つ場合もありますが、一様な初期化を行わないとデータを格納できません。

于 2011-08-11T21:27:37.570 に答える