std :: moveとstd :: forwardの違いは何ですか


160

私はここでこれを見ました: 基本クラスのMoveコンストラクターを呼び出すMoveコンストラクター

誰かが説明できますか:

  1. std::movestd::forward、好ましくは、いくつかのコード例をしますか?
  2. それを簡単に考える方法、およびいつ使用するか


「簡単に考えて、いつ使うか」価値move動かしforwardたいときや、完全転送を使いたいときに使います。これはここではロケット科学ではありません;)
Nicol Bolas

move()は無条件キャストを実行しますが、forward()は渡されたパラメーターに基づいてキャストを実行します。
RaGa__M 2016年

回答:


159

std::moveオブジェクトを受け取り、それを一時的なもの(右辺値)として扱うことができます。これは意味上の要件ではありませんが、通常、右辺値への参照を受け入れる関数はそれを無効にします。が表示std::moveされている場合は、オブジェクトの値を後で使用するべきではないことを示していますが、新しい値を割り当てて引き続き使用できます。

std::forward単一のユースケースがあります。テンプレート化された関数パラメーター(関数内)を、呼び出し元がそれを渡すために使用した値カテゴリー(lvalueまたはrvalue)にキャストします。これにより、右辺値引数を右辺値として渡すことができ、左辺値を左辺値として渡すことができます。これは、「完全転送」と呼ばれる方式です。

説明するに

void overloaded( int const &arg ) { std::cout << "by lvalue\n"; }
void overloaded( int && arg ) { std::cout << "by rvalue\n"; }

template< typename t >
/* "t &&" with "t" being template param is special, and  adjusts "t" to be
   (for example) "int &" or non-ref "int" so std::forward knows what to do. */
void forwarding( t && arg ) {
    std::cout << "via std::forward: ";
    overloaded( std::forward< t >( arg ) );
    std::cout << "via std::move: ";
    overloaded( std::move( arg ) ); // conceptually this would invalidate arg
    std::cout << "by simple passing: ";
    overloaded( arg );
}

int main() {
    std::cout << "initial caller passes rvalue:\n";
    forwarding( 5 );
    std::cout << "initial caller passes lvalue:\n";
    int x = 5;
    forwarding( x );
}

ハワードが述べているように、これらの関数はどちらも参照型にキャストするだけなので、類似点もあります。ただし、これらの特定のユースケース(右辺値参照キャストの有用性の99.9%をカバーする)以外では、static_cast直接使用して、何をしているのかについての適切な説明を書く必要があります。


私は確かにそれがいることを正確だじゃないstd::forward唯一のユースケースは、関数の引数の最適な転送です。オブジェクトメンバーなど、他のものを完全に転送したい状況に遭遇しました。
Geoff Romer、

@GeoffRomerパラメータのメンバー?例はありますか?
Potatoswatter、2013

:私は別の問題として例を掲載stackoverflow.com/questions/20616958/...
ジェフ・ローマー

うーん、これを正しく理解していれば、forward(5)が値で渡されたかのようにforward(5)が5で動作する単一の関数forward()を記述できることを意味します。明示的にオーバーロードを記述する必要がない参照。
iheanyi 14

@iheanyiうん、それはアイデアだ!しかし、それは単なる通常の関数ではなく、テンプレートでなければなりません。
Potatoswatter 14

63

どちらstd::forwardstd::move何もなく、キャストされません。

X x;
std::move(x);

上記は、xタイプXの左辺値式をタイプXの右辺値式(正確にはx値)にキャストします。 move右辺値も受け入れることができます:

std::move(make_X());

この場合、それは恒等関数です。X型の右辺値を取り、X型の右辺値を返します。

std::forwardあなたがある程度先を選択することができます。

X x;
std::forward<Y>(x);

xタイプXの左辺値式をタイプYの式にキャストします。Yの値には制約があります。

Yはアクセス可能なXのBaseまたはXのBaseへの参照です。YはXまたはXへの参照です。Cv修飾子をforwardでキャストできませんが、cv修飾子を追加できます。Yは、アクセス可能なBase変換を除いて、Xから単に変換できる型であってはなりません。

Yが左辺値参照の場合、結果は左辺値式になります。Yが左辺値参照でない場合、結果は右辺値(正確にはx値)式になります。

forwardYが左辺値参照でない場合にのみ、右辺値引数を取ることができます。つまり、右辺値を左辺値にキャストすることはできません。これは安全上の理由からです。これを行うと、参照がぶら下がってしまうためです。しかし、右辺値を右辺値にキャストすることは問題なく、許可されます。

Yを許可されていないものに指定しようとすると、エラーは実行時ではなくコンパイル時にキャッチされます。


std::forwardを使用してオブジェクトを関数に完全に転送した場合、その関数が実行された後、そのオブジェクトを使用できますか?の場合std::move、これは未定義の動作であることを認識しています。
iammilind

1
についてmovestackoverflow.com/a/7028318/576911の 場合forward、左辺値を渡すと、APIは左辺値を受け取ったかのように反応します。通常、これは値が変更されないことを意味します。しかし、それが非定数の左辺値である場合、APIがそれを変更した可能性があります。右辺値を渡す場合、これは通常、APIが右辺値から移動した可能性があることを意味するため、stackoverflow.com / a / 7028318/576911が適用されます。
Howard Hinnant

21

std::forward関数に渡されたとおりにパラメーターを転送するために使用されます。ここに示すように:

いつstd :: forwardを使用して引数を転送するのですか?

を使用std::moveすると、オブジェクトが右辺値として提供され、移動コンストラクタまたは右辺値を受け入れる関数と一致する可能性があります。それ自体が右辺値ではないstd::move(x)場合でもx、それを行います。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.