C++ 11でオブジェクトメンバー変数を初期化するこれらの方法の違いは何ですか?別の方法はありますか?どちらの方が優れていますか(パフォーマンス)?:
class any {
public:
obj s = obj("value");
any(){}
};
または
class any {
public:
obj s;
any(): s("value"){}
};
ありがとう。
いいえ、これらは同じではないです。
これらの違いはdirect-initialization vs. copy-initializationに適用されるものと同じです。
§12.6.2[class.base.init]:
mem-initializerのexpression-listまたはbraced-init-listは、指定されたサブオブジェクトを初期化するために使用されます(または、 direct-initialization。 [...]の8.5の初期化ルールによる委任コンストラクター、完全なクラスオブジェクト)
非委任コンストラクターでは、特定の非静的データメンバーまたは基本クラスがmem-initializer-idで指定されていない場合(mem-initializerがない場合を含む-listコンストラクターにctor-initializer)がなく、エンティティーが抽象クラス(10.4)の仮想基本クラスではないため、
—エンティティがbrace-or-equal-initializerを持つ非静的データメンバーである場合、エンティティは初期化されます8.5で指定;
§8.5[dcl.init]:
フォームで発生する初期化
T x = a;
同様に、引数の受け渡し、関数の戻り、例外のスロー(15.1)、例外の処理(15.3)、および集約メンバーの初期化(8.5.1)コピー初期化と呼ばれます。
member-initializer-listの非静的データメンバーの初期化は、direct-initializationのルールに従います。このルールは、移動/コピーする必要のある中間一時を作成しません。 (copy-elisionを指定せずにコンパイルした場合)、データメンバのタイプは(コピーが省略されていても)コピー/移動可能である必要はありません。さらに、direct-initializationは明示的なコンテキストを導入しますが、copy-initializationは非明示的です(初期化に選択されたコンストラクターがexplicit
の場合、プログラムはコンパイルされません)。
つまり、obj
が次のように宣言されている場合、obj s = obj("value");
構文はコンパイルされません。
_struct obj
{
obj(std::string) {}
obj(const obj&) = delete;
};
_
または:
_struct obj
{
obj(std::string) {}
explicit obj(const obj&) {}
};
_
より具体的な例として、以下はコンパイルしません:
_struct any
{
std::atomic<int> a = std::atomic<int>(1); // ill-formed: non-copyable/non-movable
std::atomic<int> b = 2; // ill-formed: explicit constructor selected
};
_
これは:
_struct any
{
std::atomic<int> a;
std::atomic<int> b{ 2 };
any() : a(1) {}
};
_
どちらの方法が優れている(パフォーマンス)?
copy-elisionを有効にすると、両方のパフォーマンスが同じになります。 copy-elisionを無効にすると、copy-initialization構文が使用されている場合(obj s = obj("value");
はの一つ)。
別の方法はありますか?
brace-or-equal-initializer構文を使用すると、direct-list-initializationも実行できます。
_class any {
public:
obj s{ "value" };
any() {}
};
_
他に違いはありますか?
言及する価値がある他のいくつかの違いは次のとおりです。
両方の例は同等です。
タイプがコピー可能または移動可能(自分でチェック)で、NRVOが実際に実行されている場合のみ(ただし、中途半端なコンパイラーであれば当然実行されます)。
多くのコンストラクターがあり、コンストラクターチェーンが不適切である場合でも、最初の方法では自分自身を繰り返すことはできません。
また、そのメソッドを使用して、C++ 14以降の(一部の)メンバーの集約初期化とは異なるデフォルトで集約を定義できます。
それらは同じです。
どちらもパフォーマンスの点で他より優れているわけではなく、それらを初期化する他の方法はありません。
クラス内の初期化(例の最初)の利点は、初期化の順序が暗黙的であることです。初期化リストでは、順序を明示的に指定する必要があります。順序が正しくないと、コンパイラは順序が正しくない初期化を警告します。
標準から:
12.6.2.5
nonstatic data members shall be initialized in the order they were declared
in the class definition
リストの順序が間違っていると、GCCから次のような苦情が寄せられます。
main.cpp: In constructor 'C::C()':
main.cpp:51:9: warning: 'C::b' will be initialized after
main.cpp:51:6: warning: 'int C::a'
初期化リストの利点はおそらく好みの問題です。リストは明示的であり、通常はソースファイル内にあります。クラス内は(ほぼ間違いなく)暗黙的であり、通常はヘッダーファイルにあります。