[英]Can I typically/always use std::forward instead of std::move?
我一直在觀看Scott Meyers 關於 C ++和2012 年之后的Universal References的演講,到目前為止一切都很有意義。 然而,一位觀眾在大約50分鍾后問了一個問題,我也在想。 邁爾斯說,他不關心答案,因為它不是慣用的,會愚蠢的想法,但我仍然感興趣。
提供的代碼如下:
// Typical function bodies with overloading:
void doWork(const Widget& param) // copy
{
// ops and exprs using param
}
void doWork(Widget&& param) // move
{
// ops and exprs using std::move(param)
}
// Typical function implementations with universal reference:
template <typename T>
void doWork(T&& param) // forward => copy and move
{
// ops and exprs using std::forward<T>(param)
}
關鍵是當我們采用右值引用時,我們知道我們有一個rvalue,所以我們應該std::move
它來保留它是一個rvalue的事實。 當我們采用通用引用( T&&
,其中T
是推導類型)時,我們希望std::forward
保留它可能是左值或右值的事實。
所以問題是:因為std::forward
保留傳遞給函數的值是左值還是右值,而std::move
只是將其參數轉換為右值,我們可以只使用std::forward
嗎? 在我們使用std::move
所有情況下, std::forward
行為都會像std::move
一樣,還是在Meyers的泛化中錯過了一些重要的行為差異?
我並不是說任何人都應該這樣做,因為正如邁耶斯所說,這完全是非慣用的,但以下也是std::move
的有效用法:
void doWork(Widget&& param) // move
{
// ops and exprs using std::forward<Widget>(param)
}
這兩者是非常不同和互補的工具。
std::move
推導出參數並無條件地創建一個rvalue表達式。 這適用於實際對象或變量。
std::forward
采用強制模板參數(你必須指定它!)並根據類型(通過添加&&
和折疊規則)神奇地創建一個左值或右值表達式。 這僅適用於推導的,模板化的函數參數。
也許以下示例說明了這一點:
#include <utility>
#include <memory>
#include <vector>
#include "foo.hpp"
std::vector<std::unique_ptr<Foo>> v;
template <typename T, typename ...Args>
std::unique_ptr<T> make_unique(Args &&... args)
{
return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); // #1
}
int main()
{
{
std::unique_ptr<Foo> p(new Foo('a', true, Bar(1,2,3)));
v.push_back(std::move(p)); // #2
}
{
v.push_back(make_unique<Foo>('b', false, Bar(5,6,7))); // #3
}
{
Bar b(4,5,6);
char c = 'x';
v.push_back(make_unique<Foo>(c, b.ready(), b)); // #4
}
}
在情況#2中,我們有一個現有的具體對象p
,我們想要無條件地從它移動。 只有std::move
才有意義。 這里沒有什么可以“前進”的。 我們有一個命名變量,我們希望從中移動。
另一方面,情境#1接受任何類型的參數列表,並且每個參數需要作為與原始調用中相同的值類別轉發。 例如,在#3中,參數是臨時表達式,因此它們將作為rvalues轉發。 但是我們也可以在構造函數調用中混合使用命名對象,如情況#4,然后我們需要轉發為左值。
是的,如果param
是一個Widget&&
,那么以下三個表達式是等價的(假設Widget
不是引用類型):
std::move(param)
std::forward<Widget>(param)
static_cast<Widget&&>(param)
通常(當Widget
可能是引用時), std::move(param)
等效於以下兩個表達式:
std::forward<std::remove_reference<Widget>::type>(param)
static_cast<std::remove_reference<Widget>::type&&>(param)
注意std::move
對於移動東西有多好。 std::forward
是它與模板類型扣除規則很好地混合:
template<typename T>
void foo(T&& t) {
std::forward<T>(t);
std::move(t);
}
int main() {
int a{};
int const b{};
//Deduced T Signature Result of `forward<T>` Result of `move`
foo(a); //int& foo(int&) lvalue int xvalue int
foo(b); //int const& foo(int const&) lvalue int const xvalue int const
foo(int{});//int foo(int&&) xvalue int xvalue int
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.