15

重複の可能性:
C++ では、値渡しと定数参照渡しのどちらが優れていますか?

C++ での値渡し、ポインター渡し、参照渡しの違いを認識しており、C++ で (const 参照ではなく) 値渡しでオブジェクトを渡すことは、ほぼ常にプログラミング エラーであると考えています。

void foo(Obj o); ... // Bad

void foo(const Obj &o); ... // Better

const 参照の代わりに値渡しが適切であると考えられる唯一のケースは、オブジェクトが参照より小さい場合であり、したがって値渡しの方が効率的です。

しかし、確かにこれは、コンパイラが決定するように構築されているようなものですか?

C++ が実際に値渡しと const 参照渡しを必要とするのはなぜですか?また、必要に応じて、コンパイラは呼び出しを const 参照へ (およびから) 自動的に変換することが許可されていますか?

((たとえば)値と参照の違いについて尋ねるC++呼び出し規約の質問は数百あるようですが、「なぜ?」と尋ねる質問は見つかりませんでした。)

4

6 に答える 6

10

const 参照よりも値渡しの方が優れている場合があるという問題は、標準のバージョンが異なれば答えも異なります。

古き良き C++03 では、数年前までは、レジスタに収まらないものはすべて const 参照で渡すことが推奨されていました。この場合、答えは次のようになります。

  • Objレジスタに収まり、値渡しと値渡しがより効率的になるので

まだ C++03 で、ここ数年 (ほぼ 10 年前にいくつかの記事でこれを推奨していたように思われるのでばかげていますが、本当のコンセンサスはありませんでした)、

  • 関数がコピーを作成する必要がある場合、コピーのソースが一時的なものである場合、インターフェイスでコピーを行うと、コンパイラはコピー省略を実行できるため、より効率的になります。

新しい C++11 標準の承認と、コンパイラによるrvalue-references のサポートの増加により、多くの場合、コピーを省略できない場合でも、再び

  • 関数がコピーを作成する必要がある場合、コピーを省略できない場合でも、それをサポートする型の場合、コンテンツが移動されます(一般的な専門用語では、オブジェクトは移動されますが、シフトされるのはコンテンツのみです)。これも、内部的にコピーするよりも効率的です。

なぜ2つの異なる呼び出し規約があるのか​​ という質問に関しては、それらには異なる目的があります。値渡しにより、関数はソース オブジェクトに干渉することなく引数の状態を変更できます。さらに、ソース オブジェクトの状態も関数に干渉しません (マルチスレッド環境と、関数がまだ実行されている間にスレッドがソースを変更することを考慮してください)。

于 2012-08-10T16:09:20.587 に答える
6

確かに、C++ に値渡しがある理由の 1 つは、それが C から継承されているためです。

第二に、あなたが指摘したように、値渡しの参照よりも小さい型の場合、効率が低下します。

ただし、あまり目立たない別のケースは、何らかの理由で引数のコピーが必要な関数がある場合です。

void foo(const Obj& obj)
{
    if(very_rare_check()) return;

    Obj obj_copy(obj);
    obj_copy.do_work();
}

この場合、コピーを強制していることに注意してください。しかし、値によって返される別の関数の結果でこの関数を呼び出すとします。

Obj bar() { return Obj(parameters); }

そしてそれを次のように呼びます:foo(bar());

const 参照バージョンを使用すると、コンパイラは 2 つのオブジェクトを作成することになります: 一時オブジェクトとコピー in foo. ただし、値渡しの場合、コンパイラはすべての一時変数を の値渡しパラメーターで使用される場所に最適化できますfoo

これに関するすばらしい記事があり、セマンティクスの一般的な移動がhttp://cpp-next.com/archive/2009/08/want-speed-pass-by-value/にあります。

最後に、特定の演算子を実装する標準的な方法は、値渡しを使用して演算子内のコピーを回避することです。

Obj operator+(Obj left, const Obj& right)
{
    return left += right;
}

これにより、オペレーターのコード自体内でコピーまたは一時オブジェクトを強制するのではなく、コンパイラーがパラメーターでコピーを生成する方法に注意してください。

于 2012-08-10T16:10:08.853 に答える
4

元のオブジェクトに影響を与えずに関数内のオブジェクトに何かをしたい場合は、値渡しします。

A minus(A b){
    b.val=-b.val;
    return b;
}
于 2012-08-10T16:02:42.690 に答える
3

コピー スワップ イディオムは、値による受け渡しを使用して、コンパイラによって生成されたコピーを実現します。

MyClass& operator=(MyClass value) // pass by value to generate copy
{
    value.swap(*this);            // Now do the swap part.
    return *this;
}

基本的に、パラメーターを変更する必要があるが、元のパラメーターには触れたくない場合に使用します。これらの状況で const 参照によって渡す場合、関数内に手動でコピーを作成する必要があります。この手動の手順により、コンパイラにコピーを処理させる場合にコンパイラが実行できる特定の最適化が妨げられます。

MyClass a;
// Some code
a = MyClass(); // reset the value of a
               // compiler can easily elide this copy.
于 2012-08-10T16:09:58.480 に答える
1

オブジェクトが変更可能である場合、値を渡すと、呼び出し元のコピーに影響を与えることなく、使用する独自のコピーが受信者に与えられ、適切な変更が行われます。常に十分に深いコピーであると想定されます。

これにより、マルチスレッドの状況での思考が単純化される場合があります。

于 2012-08-10T16:04:44.600 に答える
1

C++ が実際に値渡しと const 参照渡しを必要とするのはなぜですか? また、必要に応じて、コンパイラは呼び出しを const 参照へ (およびから) 自動的に変換することが許可されていますか?

最初に 2 番目の質問に答えましょう。

コンパイラは、コピーをパラメーターに省略できますが、一時的な右辺値を渡す場合のみです。例えば:

void foo(Obj o);

foo((Obj()))); //Extra set of parenthesis are needed to prevent Most Vexing Parse

引数パラメーターへの一時的なコピーは、コンパイラーの都合で省略される場合があります (つまり、コピーされません)。

ただし、このコピーは省略されません。

Obj a;
foo(a);

では、最初に。C++ では両方を必要とするため、異なる目的で両方を使用する場合があります。値による受け渡しは、所有権の譲渡に役立ちます。これは、オブジェクトをコピーするのではなく移動できる C++11 ではより重要です。

于 2012-08-10T16:09:30.430 に答える