8

私が働いている場所では、オブジェクトは C++ スタイルの構築 (括弧付き) を使用して初期化するのが最適であると考える人がほとんどですが、プリミティブ型は = 演算子で初期化する必要があります。

std::string strFoo( "Foo" );
int nBar = 5;

しかし、彼らがなぜこのような方法を好むのか、誰も説明できないようです。余分なコピーが必要になるため、非効率的であることがわかりますが、演算子を完全に追放し、どこでも括弧を使用std::string = "Foo";することの何が問題なのですか?=

それは共通の慣習ですか?その背後にある考えは何ですか?

4

9 に答える 9

17

= 演算子またはコンストラクター呼び出しを使用した変数の初期化は意味的に同じであり、スタイルの問題です。= 演算子の方が自然に読めるので、私は = 演算子を好みます。

= 演算子を使用しても、通常、追加のコピーは生成されません。通常のコンストラクターが呼び出されるだけです。ただし、非プリミティブ型の場合、これは宣言と同時に発生する初期化のみに適用されることに注意してください。比較:

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)などの意味的制約はすべて満たす。]

于 2008-12-09T17:57:42.883 に答える
11

別のばかげた litb 投稿の必要性を感じました。

string str1 = "foo";

これはcopy-initializationと呼ばれます。これは、一時変数を省略しない場合、コンパイラが行うことは次のとおりであるためです。

string str1(string("foo")); 

使用される変換コンストラクターが暗黙的であることを確認することに加えて。実際、すべての暗黙的な変換は、コピーの初期化に関して標準で定義されています。型 U から型 T への暗黙的な変換が有効であると言われます。

T t = u; // u of type U

有効です。

対照的に、

string str1("foo");

書かれていることを正確に実行しており、直接初期化と呼ばれます。また、明示的なコンストラクターでも機能します。

ところで、-fno-elide-constructors を使用して、一時変数の省略を無効にすることができます。

-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 の型がプリミティブ型の場合。したがって、両方の形式を使用できます。人々が 2 番目の形式よりも 1 番目の形式を使用するようになるのは、そのスタイルだと思います。


一部の人々は、宣言のあいまいさを解消したいため、状況によっては最初のものを使用する場合があります。

T u(v(a));

と呼ばれるコンストラクターのパラメーターを取得するu一時的な型を使用して初期化される変数の定義として誰かに見えるかもしれません。しかし実際には、コンパイラがそれに対して行うことは次のとおりです。va

T u(v a);

type の引数を取り、vというパラメータを持つ関数宣言を作成しますa。だから人々はそうする

T u = v(a);

たとえ彼らができたとしても、それを明確にするために

T u((v(a)));

また、関数パラメータの周りに括弧がないため、コンパイラはそれを関数宣言ではなく変数定義としても読み取ります:)

于 2008-12-09T18:21:34.147 に答える
4

パフォーマンスに関して重要であることを証明していない限り、例の代入演算子を使用した余分なコピーについて心配する必要はありません ( std::string foo = "Foo";)。最適化されたコードを見ると、そのコピーが存在する場合でもかなり驚くでしょう。実際には、適切なパラメーター化されたコンストラクターが呼び出されると思います。

あなたの質問への答えとして、はい、それはかなり一般的な規則だと思います。古典的に、人々は代入を使用して組み込み型を初期化しており、伝統を変更するやむを得ない理由はありません。読みやすさと習慣は、最終的なコードにほとんど影響を与えないことを考えると、この規則の完全に正当な理由です。

于 2008-12-09T17:59:16.937 に答える
2

おそらく、次のようなコードが見つかります。

std::string strFoo = "Foo";

余分なコピーの実行を回避し、括弧付きのコードと同じコード (単一引数のコンストラクターの呼び出し) にコンパイルします。

一方、コンストラクターメンバーの初期化リストなど、括弧を使用する必要がある場合があります。

= または括弧を使用してローカル変数を作成することは、主に個人的な選択の問題だと思います。

于 2008-12-09T17:57:58.920 に答える
1

まあ、彼らが何を考えているかは誰にもわかりませんが、主にオブジェクトではないため、プリミティブ型の = を好みます。これは、それらを初期化する「通常の」方法です。

于 2008-12-09T17:58:06.017 に答える
0

スタイルの問題です。「std::string = "Foo"; 余分なコピーが含まれるため、効率が悪い」というステートメントでさえ正しくありません。この「余分なコピー」はコンパイラによって削除されます。

于 2008-12-09T17:57:05.100 に答える
0

私はそれが習慣だと信じています。 = を使用して初期化できるオブジェクトはほとんどありません。文字列はその1つです。これは、「どこでも括弧を使用する(言語で使用できるようにする)」と言ったことを行う方法でもあります。

于 2008-12-09T17:59:23.723 に答える
0

1 つの引数:

std::string foo("バー");

引数の数が変わっても、物事は同じままです。つまり、

std::string foo("バー", 5);

「=」記号では機能しません。

もう 1 つのことは、多くのオブジェクトで「=」が不自然に感じられることです。たとえば、引数が長さを与える Array クラスがあるとします。

配列 arr = 5;

値が 5 ではなく長さが 5 の Array を作成していないため、気分が悪くなります。

配列 arr(5);

値をコピーするだけでなく、指定されたパラメーターでオブジェクトを構築しているため、より自然に感じられます。

于 2008-12-09T18:36:15.910 に答える
0

しかし、さらに混乱させるために、オブジェクト構文を使用して初期化リストでプリミティブを初期化します。

foo::foo()   
  ,anInt(0)   
  ,aFloat(0.0)   
{   
}   
于 2008-12-09T18:44:33.460 に答える