13

このプログラムは、関数から文字列を移動し、それを別の文字列の作成に使用しようとします。

#include <iostream>
#include <string>
#include <utility>

std::string && Get_String(void);

int main(){

    std::string str{Get_String()};
    std::cout << str << std::endl;

    return 0;
}

std::string && Get_String(void){

    std::string str{"hello world"};

    return std::move(str);
}

プログラムはコンパイルされますが、実行時にsegfaultが発生します。


これが私の理論的根拠でした: Get_Stringローカル文字列を作成します。文字列がスコープ外になる前に、その文字列のコピーを作成して返す必要があります。そのコピーは、mainで文字列を作成するために使用されます。ただし、文字列を関数の外に移動した場合は、コピーを作成する必要はありません。

移動のセマンティクスを理解するために、誰かが私が行っていることがおそらく意味をなさない理由を説明できますか?関数の外にオブジェクトを移動することは可能ですか?


編集:
関数のシグネチャを次のように変更すると、正しくコンパイルされて実行されます。

std::string && Get_String(void);

std::string Get_String(void);  

この場合、リターン中に文字列を移動する方がさらに効率的ですか?

4

2 に答える 2

19

この例を考えると、

X foo ()
{
  X x;    
  return x;
}

次の動作が保証されています。

Xアクセス可能なコピーまたは移動コンストラクターがある場合、コンパイラーはコピーを削除することを選択できます。これは、いわゆる(名前付き)戻り値最適化((N)RVO)であり、C ++ 11より前でも指定されており、ほとんどのコンパイラーでサポートされています。
•それ以外の場合X、moveコンストラクターがある場合は移動されxます。
•それ以外の場合X、コピーコンストラクターがある場合xはコピーされます。
•それ以外の場合は、コンパイル時エラーが発生します。

返されたオブジェクトがローカルの非静的オブジェクトである場合、右辺値参照を返すことはエラーであることに注意してください。

X&& foo ()
{
  X x;
  return x; // ERROR: returns reference to nonexistent object
}

右辺値参照は参照であり、ローカルオブジェクトを参照しながらそれを返すことは、もう存在しないオブジェクトへの参照を返すことを意味します。使用するかどうかstd::move()は関係ありません。

std::move()実際にはオブジェクトを移動しません。左辺値を右辺値に変換するだけです。

于 2012-11-29T03:57:25.803 に答える
3

そのGet_String関数は、右辺値参照を関数ローカルオブジェクトにバインドします。右辺値参照は、破壊されようとしているものには役立ちますが、すでに破壊されているものの左辺値参照と同じくらい悪いです。

ローカルオブジェクトを関数から移動するには、クラスタイプごとに戻ります。

std::string Get_String(void) {
    std::string str{"hello world"};
    return str;
}

コンパイラがコピー/移動を完全に排除できない場合、呼び出し元が取得する戻り値は、戻り式が次の場合に限り、コピーコンストラクタではなく移動コンストラクタを使用して作成されます。

  • 一時的、または
  • 自動保存期間(上記のように)で何かに名前を付ける単一の識別子str、または
  • std::move(something)

(したがってreturn std::move(str);、明示的にする必要がある場合もありますが、ここでは必要ありません。)

于 2012-11-29T03:58:04.570 に答える