簡體   English   中英

我可以/通常使用std :: forward而不是std :: move嗎?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM