なぜC ++でプリミティブ型を初期化する=でしょうか?
-
20-08-2019 - |
質問
私は、人々は、ほとんどのオブジェクトは最高のC ++を使用して初期化されると思い、仕事はどこ - (括弧付き)スタイルの構築、プリミティブ型は=演算子で初期化されなければならないのに対します:
std::string strFoo( "Foo" );
int nBar = 5;
誰もが、彼らは物事を好む理由しかし、この方法を説明することはできていないようにみえます。私はそれが余分なコピーを伴うだろうのでstd::string = "Foo";
が非効率的になることがわかりますが、何がちょうど完全に=
演算子を追放し、どこでも、括弧を使用してが悪いの?
これは一般的な慣習ですか?その背後にある考え方は何でしょうか。
解決
をあなたはそれがパフォーマンスに関して重要なことを証明してきた場合を除き、私はあなたの例(std::string foo = "Foo";
)の代入演算子を使用して余分なコピーを心配しないでしょう。そのコピーもあなたが最適化されたコードを見てみたら、私はそれが実際に適切なパラメータ化コンストラクタを呼び出すと信じ存在する場合、私はかなり驚かれることと思います。
あなたの質問への答えは、はい、私はそれはかなり一般的な慣例だと言うだろう。古典的には、人々は、組み込み型を初期化するために割り当てを使用している、と伝統を変更するには、説得力のある理由がありません。読みやすさと習慣は、それが究極のコードを持っているかはほとんど影響与えられたこの大会のために完全に有効な理由があります。
他のヒント
=演算子またはコンストラクタの呼び出しと変数の初期化はそれだけでスタイルの問題だ、意味的に同じです。それは、より自然に読み込むので、私は、=演算子を好むます。
=演算子を使用して、の通常の余分なコピーを生成しません - それだけで通常のコンストラクタを呼び出します。非プリミティブ型と、これが唯一の宣言と同時に起こる初期化するためのものであること、しかし、注意してください。比較ます:
std::string strFooA("Foo"); // Calls std::string(const char*) constructor
std::string strFoo = "Foo"; // Calls std::string(const char*) constructor
// This is a valid (and standard) compiler optimization.
std::string strFoo; // Calls std::string() default constructor
strFoo = "Foo"; // Calls std::string::operator = (const char*)
あなたは非自明なデフォルトコンストラクタを持っている場合は、後者の建設は少し非効率的になることがあります。
C ++標準の、セクション8.5 、段落14本の状態:
そうでない場合(すなわち、残りのコピーの初期化の場合のために)、一時的に作成されます。宛先タイプまたはその(13.3.1.4)を列挙している派生クラス、およびベスト一方にソース・タイプから変換することができ、ユーザ定義の変換配列は、オーバーロード解決(13.3)を介して選択されます。そう選択したユーザ定義の変換は、CV-修飾子と、そのタイプのユーザ定義の変換関数の呼び出しによって返されたタイプであり、一時的に初期化式を変換するために呼び出され 宛先タイプの。変換が行われるか曖昧であることができない場合は、初期化が悪い形成されています。初期化されているオブジェクトは、直接初期化され 上記の規則に従って一時から 87 )の特定の場合には、実装は、直接オブジェクトを初期化することによって一時的に排除することが許されます。 12.2を参照してください。の
セクション12.2の状態のパートます:
一時オブジェクトが作成されたかのように一時オブジェクトの作成が回避されたとしても、すべての意味上の制限は尊重されなければなりません。 [例: コピーコンストラクタが呼び出されていない場合でも、そのようなアクセス(11)のように、すべての意味上の制限は、満足しなければなりません。
私はちょうど別の愚かなlitbのポストのための必要性を感じています。
string str1 = "foo";
コンパイラが何をするか、それは任意の一時をElideのいない場合は、であるため、は、をコピー初期化のと呼ばれます:
string str1(string("foo"));
の横に使用する変換コンストラクタが暗黙的であることを確認します。実際には、すべての暗黙の変換は、コピーの初期化の点では標準で定義されています。
場合には、Tを入力するタイプUからの暗黙の変換が有効であると言われていますT t = u; // u of type U
は有効です。
constrastにおいて、
string str1("foo");
が書かれている正確に何やっている、との直接の初期化のと呼ばれています。また、明示的なコンストラクタで動作します。
ところで、あなたは-fno-Elideの-コンストラクタを使用して一時のelidingを無効にすることができます:
-fno-elide-constructors
The C++ standard allows an implementation to omit creating a temporary which
is only used to initialize another object of the same type. Specifying this
option disables that optimization, and forces G++ to call the copy constructor
in all cases.
<時間>
標準
との間に実質的に差がないと言いますT a = u;
と
T a(u);
TおよびUの種類がプリミティブ型である場合。だから、両方の形式を使用することができます。私はそれは、人々はむしろ二よりも第1の形式を使用します、それだけのスタイルだと思います。
<時間>彼らは宣言を明確にしたいため、一部の人々は、いくつかの状況で最初に使用することができます:
T u(v(a));
と呼ばれるu
そのコンストラクタのパラメータを取得するタイプv
の一時的なを使用して初期化されている変数a
の定義として誰かに見migh。しかし、実際には、どのようなコンパイラはそれを行うことはこれです:
T u(v a);
これはと<=>と呼ばれるパラメータを使用して、<=>型の引数を取る関数宣言を作成します。人々が行うので、
T u = v(a);
、彼らが行っている可能性もかかわらず、それを明確にするために、
T u((v(a)));
関数のパラメータの周りがカッコされることはありませんので、あまりにも、コンパイラはあまりに代わりに、関数宣言の変数の定義として、それを読んでいました:)
あなたはおそらく、このような
とそのコードを見つけますstd::string strFoo = "Foo";
余分なコピーを行うことを避けると括弧付きのものと同じコード(単一引数のコンストラクタの呼び出し)にコンパイルされます。
一方、一つはしなければならないのようなコンストラクタメンバ初期化リストとして括弧を使用する場合がある。
私は、ローカル変数を構築する=または括弧の使用は、主に個人の選択の問題だと思います。
さて、誰がを知っている彼らは、を、彼らはオブジェクトではありません、それはそれらを初期化する「通常」の方法ですので、主な理由は、と思うが、私はまた、プリミティブ型の=を好む。
これはスタイルの問題です。 「STD ::文字列= 『fooがあることさえ声明』、それは余分なコピーを伴うだろうので、非効率的だろうが、」正しくありません。この「余分なコピーは、」コンパイラによって削除されます。
私はそれが習慣の多くをであると信じて、非常に少数のオブジェクトは=を使用して初期化することができ、文字列は、そのうちの一つです。それは、「(言語は、あなたがそれを使用することを可能にすること)どこでもカッコを使用して、」また、あなたが言ったことを行うための方法です。
1のために作ることができることを一つの引数:
のstd ::文字列のfoo( "バー");
それも引数の数の変化、すなわち場合、同じものを保持していることです:ます。
のstd ::文字列のfoo( "バー"、5);
'=' 記号では動作しません。
もう一つは、多くのオブジェクトのために「=」たとえばあなたは引数が長さを与えるArrayクラスを持っていると言う、不自然に感じているということです。
アレイARR = 5;
我々は値5でアレイを構築していないので、、良い感じませんが、長さが5でます:
アレイARR(5)
あなただけの値をコピーしていない、指定されたパラメータを持つオブジェクトを構築しているので、、より自然に感じます。
しかし、その後、ちょうどあなたも、より多くのあなたがオブジェクト構文を使用して初期化リストでプリミティブを初期化混同します。
foo::foo()
,anInt(0)
,aFloat(0.0)
{
}