6

C ++(暗黙的または明示的)値コンストラクターは、引数のコピーをオブジェクトに格納する必要がある場合、値またはconstへの参照によってパラメーターを受け入れる必要がありますか?

これが私が考えることができる最短の例です:

struct foo {
    bar _b;
    foo(bar [const&] b) // pass by value or reference-to-const?
        : _b(b) { }
};

ここでの考え方は、fooオブジェクトが作成されるさまざまな方法のいずれかで、fooオブジェクトが作成されるときにbarのコピーコンストラクターの呼び出しを最小限に抑えたいということです。

私はコピーの省略と(名前付き)戻り値の最適化について少し知っていて、「スピードが欲しいですか?値を渡す」を読んだことに注意してください。ただし、この記事ではこのユースケースを直接扱っているとは思いません。

編集:私はより具体的にする必要があります。

、または基本的な組み込み型であるsizeof(bar)かどうかがわからないとします(テンプレートパラメータの場合もあれば、クラスではなくクラステンプレートの場合もあります)。また、'コンストラクターをインライン化できる(または、さらに言えば's )ことができると思い込まないでください。少なくとも、RVOを実装するコンパイラを使用している可能性があると想定してください。barbarfoofoobar

私が望んでいるのは、このような呼び出しが(初期化リストで実行しbarている場合でも)コピーコンストラクターへの呼び出しをまったく呼び出さない可能性があることです(コンパイラーの最適化が与えられた場合)。_b(b)foo

foo f = function_that_creates_and_returns_a_bar_object_using_rvo();

foo(C ++ 98標準を前提として)これを実行できる可能性はありますか?その場合、値ではなく参照から定数へのパラメーターを受け入れると、多かれ少なかれ機能する可能性がありますか?

4

8 に答える 8

5

すべてが等しい場合、十分に複雑なクラスには const& を渡し、POD と単純なオブジェクトには value を渡します。

従来の値渡しではなく const 参照渡しの長所と短所を説明する

良い点:

  • コピーを回避します (高価なコピーを持つオブジェクトには大きなプラス)
  • 読み取り専用アクセス

ネガ:

  • 彼らが本当にしたいのであれば、誰かが const を参照から const キャストすることができます

さらに重要なことは、コピーがいつ発生するかを明示的に制御することです(あなたの場合、初期化子リストで初期化 _b を渡すとき)。マイナス面を考えると… リスクがあることに同意します。ほとんどすべての優れたプログラマーは、const_cast を使用することについて汚いと感じると思います。さらに、 const_cast を忠実に検索し、引数から const をキャストする人にトマトを投げることができます。しかし、タカのようにコードを見る時間があるのは誰ですか :)?

私の主観的な意見では、十分に複雑なクラスやパフォーマンスが重要な環境では、コピー コンストラクターを回避する利点がリスクを上回るというものです。ただし、本当に馬鹿げた POD クラスの場合は、データのコピーを作成して値渡しする傾向があります。

于 2009-12-04T15:59:35.727 に答える
4

この質問を見てください。

const T & argifsizeof(T)>sizeof(void*)を使用し、T argifを使用しますsizeof(T) <= sizeof(void*)。すべての基本型は、この規則の例外でなければなりません

于 2009-12-04T15:57:50.537 に答える
2

標準の内容を確認していませんが、これを経験的に試すことで、コンストラクターが引数を値で受け入れるかconst参照で受け入れるかに関係なく、GCCはコピーを最適化しないことがわかります。

ただし、コンストラクターがconst参照を取得する場合、既存のバーオブジェクトからfooを作成するときに、コンストラクターは不要なコピーを回避できます。

要約する:

Bar b = makeBar();         // No copy, because of RVO
FooByValue f1 = makeBar(); // Copy constructor of Bar called once
FooByRef f2 = makeBar();   // Copy constructor of Bar called once
FooByValue f3(b);          // Copy constructor of Bar called twice
FooByRef f4(b);            // Copy constructor of Bar called only once

私がコンパイラの専門家であるというわけではありませんが、通常、戻り値を任意の場所(fooオブジェクトのメンバーフィールドなど)にRVOすることはできないのは理にかなっていると思います。代わりに、ターゲットがスタックの一番上にある必要があります。

于 2009-12-04T19:41:08.340 に答える
2

スタイル的には、参照渡しの方が良い方法だと思います。

パフォーマンスが本当に重要な場合は、推測しないでください。それを測定します。

于 2009-12-04T16:20:36.523 に答える
2

C++98 および C++03 では、渡してからコピーする必要がconst& barあります。C++0x では、渡してから move を実行する必要がbarあります ( barmove コンストラクターがある場合)。

#include <utility>

struct foo
{
    bar _b;

    foo(bar b) : _b(std::move(b)) {}
};

左辺値パラメーターを使用して foo を構築すると、コピー コンストラクターが呼び出されて copy が作成されb、そのコピーが に移動され_bます。右辺値パラメーターを使用して foo を作成すると、barの move コンストラクターが呼び出されて に移動しb、その後再び に移動し_bます。

于 2009-12-04T19:55:34.223 に答える
1

私はbarフォームの通常のコピーコンストラクタを持っていると仮定していますbar(const bar &b)

ここで const 参照を取ります。barのコンストラクターはその参照を取得し、コピーを実行します。総部数: 1.

参照をオフのままにしておくと、コンパイラは b のコピーを作成し、そのコピーをfooのコンストラクターに渡し、コンストラクターはそれを に渡してbarコピーします。

于 2009-12-04T15:58:21.863 に答える
-1

クラスで bar への shared_pointer を保持し、そのように渡します。そうすれば、コピーコンストラクターを呼び出すことはありません:)。

于 2009-12-04T15:57:16.607 に答える