web-dev-qa-db-ja.com

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

私はここでこれを見ました: ベースクラスのMove Constructorを呼び出すMove Constructor

誰か説明できますか:

  1. std::move および std::forward 、できればいくつかのコード例を使用して?
  2. 簡単にそれを考える方法、そしていつ使用するか
152
aCuria

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

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

宛先 説明

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を直接使用して、何をしているのかについての適切な説明を書く必要があります。

144
Potatoswatter

両方 std::forwardおよびstd::moveはキャストに他なりません。

X x;
std::move(x);

上記は、型Xの左辺値式xを、型Xの右辺値式(正確にはxvalue)にキャストします。 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への参照です。forwardでcv修飾子をキャストすることはできませんが、cvを追加できます-修飾子。 Yは、アクセス可能なBase変換を除き、Xから単に変換可能な型にすることはできません。

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

forwardは、Yが左辺値参照でない場合にのみ右辺値引数を取ることができます。つまり、右辺値を左辺値にキャストすることはできません。これは安全性の理由によるものです。これを行うと、一般的に参照がぶら下がります。しかし、右辺値を右辺値にキャストすることは問題ありません。

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

60
Howard Hinnant

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

引数を転送するためにstd :: forwardを使用する場合

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

21
Bo Persson