web-dev-qa-db-ja.com

関数の戻り時にstd :: stringで移動セマンティクスを使用する方法

可能性のある複製:
C++ 11右辺値と移動の意味論の混乱

私が正しいと思うのは

std::string GetLine()
{
std::string str;
std::getline(std::cin, str);
return std::move(str);
}

しかし、このリンクでは http://www.cprogramming.com/c++11/rvalue-references-and-move-semantics-in-c++11.html (ヘッダー部分を確認してください。関数からの明示的な右辺値参照)

これは、移動セマンティクスの#1グーグル検索ヒットであり、同様の関数シグネチャを示します。

int&& GetInt()
{
int x = 0;
// code here
return std::move(x);
}

私が他の場所で読んだものから&&は右辺値referenceを意味するので、この場合は存在しないオブジェクトへの参照を返します。

どっち?

(はい、intを移動することには実質的なメリットがないことを知っていますが、問題は、最初の関数でstd :: stringまたはstd :: string &&の戻り値の型を使用するかどうかです。それがすべての型に対してどのように行われるかです。)

19
EddieV223

int&& GetInt()の例が間違っており、破棄されたオブジェクトへの参照を返していることは間違いありません。ただし、見逃していない限り、投稿したリンクには実際にはlocal変数への参照を返すコードは表示されません。代わりに、返されるglobal変数への参照が表示されますが、これは問題ありません。

戻るときに移動セマンティクスを使用する方法を次に示します。

_std::string func()
{
    std::string rv;
    /* ... */
    return rv;
}
_

通常、オブジェクトを返すときにstd::move()を使用しないでください。これは、RVOが発生する可能性があるときはいつでも移動が暗黙的に許可されており、std::move()を使用するとRVOが抑制されるためです。そのため、std::move()を使用することは決して良くなることはなく、通常の状態に戻るよりも悪い場合がよくあります。


繰り返しになりますが、std::move()を使用すると、戻り値の最適化が抑制されるため、単に返される変数に名前を付けるよりも悪くなる可能性があります。戻り値の最適化により、オブジェクトをコピーする必要なく、呼び出し元にオブジェクトを返すことができます

クラスの戻り値の型を持つ関数のreturnステートメント内。式がcv非修飾型と同じ非揮発性自動オブジェクト(関数またはcatch-clauseパラメーター以外)の名前である場合関数の戻り値の型。自動オブジェクトを関数の戻り値に直接作成することで、コピー/移動操作を省略できます。

— [class.copy] 12.8/31

ただし、std::move()を使用すると、戻り式が名前で返されるオブジェクトの名前になるのを防ぎます。代わりに、式はより複雑になり、言語は特別な処理を行うことができなくなります。

オブジェクトに名前を付けるだけでstd::move()を使用するよりも悪くないのは、式がstd::move()を必要とせずにすでに右辺値として処理できるという別のルールがあるためです。

ソースオブジェクトが関数パラメーターであり、コピーされるオブジェクトが左辺値で指定されているという事実を除けば、コピー操作の除外の基準が満たされるか満たされる場合、コピーのコンストラクターを選択するためのオーバーロード解決は次のとおりです。最初に、オブジェクトが右辺値で指定されているかのように実行されます。

33
bames53

質問への回答、並​​べ替え:stringを返します。何もmoveせず、RVOを使用(依存)してください。

std::string func()
{
    std::string rv;
    /* ... */
    return rv;
}

これが一般的に行われる方法です。一時変数への(r値かどうかにかかわらず)参照を返すことはできません。

9
eq-

strがローカル変数の場合、return std::move(str);と言う必要はありません。変数が戻り値の最適化の基準を満たしている場合、returnステートメントで変数がバインドされます右辺値参照。

また、lvalueまたはrvalue参照ではなく、ローカル変数への参照を返さないように注意してください。

すべてが言った、あなたは持っているべきです:

int foo() { int x; /*...*/ return x; }

std::string bar() { std::string str; /*...*/ return str; }
4
Kerrek SB