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);
}
テンプレート引数を明示的に指定せずに_std::forward
_を使用することはできませんcannotそれは非演context的な文脈で意図的に使用されます。
これを理解するには、転送参照(推定T
の_T&&
_)が内部でどのように動作するかを本当に理解する必要があります。それではそれを見てみましょう。
_template <class T>
void foo(T &&t)
{
bar(std::forward<T>(t));
}
_
次のようにfoo
を呼び出したとしましょう:
_foo(42);
_
_42
_は、int
型の右辺値です。 T
はint
に推定されます。したがって、bar
の呼び出しは、int
を_std::forward
_のテンプレート引数として使用します。 _std::forward<U>
_の戻り型は_U &&
_です。この場合、それは_int &&
_なので、t
は右辺値として転送されます。
それでは、次のようにfoo
を呼び出しましょう。
_int i = 42;
foo(i);
_
i
はint
型の左辺値です。完全な転送のための特別なルールのため、タイプV
の左辺値を使用してタイプ_T &&
_のパラメーターでT
を推定する場合、_V &
_が推定に使用されます。したがって、この場合、T
は_int &
_と推定されます。
したがって、_int &
_のテンプレート引数として_std::forward
_を指定します。したがって、戻り値の型は「_int & &&
_」になり、_int &
_になります。それは左辺値なので、i
は左辺値として転送されます。
概要
これがテンプレートで機能するのは、_std::forward<T>
_を実行する場合、T
が参照である場合(元が左辺値の場合)とそうでない場合(元が右辺値の場合)です。したがって、_std::forward
_は、必要に応じて左辺値または右辺値参照にキャストされます。
1つのタイプしか使用できないため、非テンプレートバージョンでこの機能を実行することはできません。 setImage(Image&& image)
は左辺値をまったく受け入れないという事実は言うまでもありません。左辺値は右辺値参照にバインドできません。
「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);
}
ユニバーサル参照(転送参照):この関数はすべてを受け入れ、完全な転送を行います。
std::forward
でテンプレートタイプを指定する必要があります。
このコンテキストでは、Image&& image
はalways r値参照であり、std::forward<Image>
は常に移動するため、std::move
を使用することもできます。
R値参照を受け入れる関数はl値を受け入れることができないため、最初の2つの関数と同等ではありません。