web-dev-qa-db-ja.com

std :: forwardとstd :: moveの使用

std::forwardはテンプレートパラメータでのみ使用されることを常に読んでいます。しかし、私はその理由を自問していました。次の例を参照してください。

void ImageView::setImage(const Image& image){
    _image = image;
}

void ImageView::setImage(Image&& image){
    _image = std::move(image);
}

これらは基本的に同じことを行う2つの関数です。 1つはl値参照を取り、もう1つはr値参照を取ります。ここで、引数がl値参照の場合はstd::forwardはl値参照を返し、引数が1の場合はr値参照を返すことになっているので、このコードは次のように簡略化できます。

void ImageView::setImage(Image&& image){
    _image = std::forward(image);
}

これは、cplusplus.comがstd::forwardに言及している例(テンプレートパラメーターなし)に似ています。これが正しいかどうか、そしてそうでない場合は理由を知りたいだけです。

私はまた、何が違うのかを自問していました

void ImageView::setImage(Image& image){
    _image = std::forward(image);
}
52
bweber

テンプレート引数を明示的に指定せずに_std::forward_を使用することはできませんcannotそれは非演context的な文脈で意図的に使用されます。

これを理解するには、転送参照(推定Tの_T&&_)が内部でどのように動作するかを本当に理解する必要があります。それではそれを見てみましょう。

_template <class T>
void foo(T &&t)
{
  bar(std::forward<T>(t));
}
_

次のようにfooを呼び出したとしましょう:

_foo(42);
_

_42_は、int型の右辺値です。 Tintに推定されます。したがって、barの呼び出しは、intを_std::forward_のテンプレート引数として使用します。 _std::forward<U>_の戻り型は_U &&_です。この場合、それは_int &&_なので、tは右辺値として転送されます。

それでは、次のようにfooを呼び出しましょう。

_int i = 42;
foo(i);
_

iint型の左辺値です。完全な転送のための特別なルールのため、タイプVの左辺値を使用してタイプ_T &&_のパラメーターでTを推定する場合、_V &_が推定に使用されます。したがって、この場合、Tは_int &_と推定されます。

したがって、_int &_のテンプレート引数として_std::forward_を指定します。したがって、戻り値の型は「_int & &&_」になり、_int &_になります。それは左辺値なので、iは左辺値として転送されます。

概要

これがテンプレートで機能するのは、_std::forward<T>_を実行する場合、Tが参照である場合(元が左辺値の場合)とそうでない場合(元が右辺値の場合)です。したがって、_std::forward_は、必要に応じて左辺値または右辺値参照にキャストされます。

1つのタイプしか使用できないため、非テンプレートバージョンでこの機能を実行することはできません。 setImage(Image&& image)は左辺値をまったく受け入れないという事実は言うまでもありません。左辺値は右辺値参照にバインドできません。

71
Angew

「Effective Modern C++」を読むことをお勧めします。著者はScott Meyersです。

項目23:std :: moveおよびstd :: forwardを理解する。

項目24:右辺値参照のユニバーサル参照を区別します。

純粋に技術的な観点から言えば、答えはイエスです。std :: forwardはすべてを実行できます。 std :: moveは必要ありません。もちろん、キャストはどこにでも書くことができるので、どちらの機能も本当に必要ではありませんが、それは、うんざりすることに同意することを望みます。 std :: moveの魅力は、利便性、エラーの可能性の低減、および明確さの向上です。

rvalue-reference:この関数は右辺値を受け入れ、左辺値を受け入れることはできません。

void ImageView::setImage(Image&& image){
    _image = std::forward(image); //error 
    _image = std::move(image);//conventional
    _image = std::forward<Image>(image);//unconventional

}

最初に、std :: moveには関数引数のみが必要であり、std :: forwardには関数引数とテンプレート型引数の両方が必要であることに注意してください。

template <typename T> void ImageView::setImage(T&& image){
    _image = std::forward<T>(image);
}

ユニバーサル参照(転送参照):この関数はすべてを受け入れ、完全な転送を行います。

22
Ron Tang

std::forwardでテンプレートタイプを指定する必要があります。

このコンテキストでは、Image&& imagealways r値参照であり、std::forward<Image>は常に移動するため、std::moveを使用することもできます。

R値参照を受け入れる関数はl値を受け入れることができないため、最初の2つの関数と同等ではありません。

7
Chris Drew