4

次の型Xと機能がありfます。

struct X { ... };

X f() { ... };

ここで、別の関数の 3 つの代替定義を考えますg

(1)

void g()
{
    X x = f();

    ...
}

(2)

void g()
{
    X& x = f();

    ...
}

(3)

void g()
{
    X&& x = f();

    ...
}

3 つの異なるケース間で定義された動作 (または潜在的な動作) の違いは何ですか? (プレースホルダー付きの '...' コードは 3 つのケースで同一であると仮定します)

アップデート:

gが返された場合はどうXなりますか : 次は合法で正しいですか?

X g()
{
    X&& x = f();

    ...

    return move(x);
}

(移動は必要ですか、それは何かをしますか?)

以下が同じコードを生成するように、RVO が連鎖すると思いますか?

X g()
{
    X x = f();

    ...

    return x;
}
4

2 に答える 2

6

2 違法です。他の 2 つは本質的に同一です。戻り時に寿命が終了するx型の変更可能な左辺値です。Xg()

もちろん、厳密には、最初のケースはムーブ コンストラクターを呼び出します (ただし、いくつかの RVO/NRVO アクションの最有力候補です)。そうではなく、最初のものはより高価かもしれません。ただし、コンパイラのオプションと不動の型の現実は、これはほぼ完全に技術的なものであり、そのようなケースを実際に示していただければ驚くでしょう。

于 2013-01-07T00:08:17.877 に答える
1

f()は値によって返されるため、prvalueです。

  1. これは、 type の prvalue であるX式で初期化されたtype の新しいオブジェクトを作成します。新しいオブジェクトがどのように構築されるかは、移動コンストラクターがあるかどうかによって異なります。移動コンストラクター (または右辺値を受け入れるテンプレート コンストラクター) がある場合は呼び出され、それ以外の場合は呼び出されるコピー コンストラクターがあります。実際には、コンパイラーはほぼ確実にコンストラクター呼び出しを省略しますが、適切なコンストラクターにアクセスでき、削除されないようにする必要があります。f()XXX

  2. これはコンパイルさえしません。投稿する前に試していないため、反対票が入ります!

  3. これは type の新しい参照を作成し、X&&それを によって返される prvalue にバインドして初期化しf()ます。その prvalue の有効期間は、参照と同じ有効期間に延長されますx

移動/コピーが省略されていると仮定すると、動作の違いはおそらく何もありませんが、(1) と (3) の間にセマンティクスの違いがあります。これは、一方が失敗する可能性があるコンストラクターのオーバーロード解決を行い、もう一方が常に機能するためです。

g が X を返した場合はどうなりますか? 次の記述は合法で正しいですか?

それは合法です。は不要です。moveローカル変数を値で返すときに、変数が右辺値であるかのようにコンストラクターのルックアップが最初に行われるという特別な規則がありXますmove(x)。RVO は「連鎖」する必要があります。g返された場合X&、バインド先のオブジェクトが の最後でスコープ外になるため、どちらの場合でも問題が発生しますg

(注意: std::moveADL を防止するために常に修飾することをお勧めしstd::forwardます。同様に、.std::movestd::forwardmoveforwardswap


SO に関する質問をして C++ を学習する代わりに、コードを記述して何が起こるかをテストし、それを自分で証明してみませんか? G++ では、フラグを使用-fno-elide-constructorsしてコンストラクタの省略をオフにして、省略がない場合に何が起こるかを確認できます。また、そのフラグを使用しない場合 (デフォルト)、RVO が自分で「チェーン」するかどうかを簡単にテストできます。

于 2013-01-07T22:27:49.170 に答える