Usage of std::forward vs std::move

前端 未结 3 1661
隐瞒了意图╮
隐瞒了意图╮ 2020-12-04 09:53

I always read that std::forward is only for use with template parameters. However, I was asking myself why. See the following example:

void Imag         


        
相关标签:
3条回答
  • 2020-12-04 10:16

    I recommend reading "Effective Modern C ++" by Scott Meyers, specifically:

    • Item 23: Understand std::move and std::forward.
    • Item 24: Distinguish universal references for rvalue references.

    From a purely technical perspective, the answer is yes: std::forward can do it all. std::move isn’t necessary. Of course, neither function is really necessary, because we could write casts everywhere, but I hope we agree that that would be, well, yucky. std::move’s attractions are convenience, reduced likelihood of error, and greater clarity.

    rvalue-reference

    This function accepts rvalues and cannot accept lvalues.

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

    Note first that std::move requires only a function argument, while std::forward requires both a function argument and a template type argument.

    Universal references (forwarding references)

    This function accepts all and does perfect forwarding.

    template <typename T> void ImageView::setImage(T&& image){
        _image = std::forward<T>(image);
    }
    
    0 讨论(0)
  • 2020-12-04 10:29

    You cannot use std::forward without explicitly specifying its template argument. It is intentionally used in a non-deduced context.

    To understand this, you need to really understand how forwarding references (T&& for a deduced T) work internally, and not wave them away as "it's magic." So let's look at that.

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

    Let's say we call foo like this:

    foo(42);
    
    • 42 is an rvalue of type int.
    • T is deduced to int.
    • The call to bar therefore uses int as the template argument for std::forward.
    • The return type of std::forward<U> is U && (in this case, that's int &&) so t is forwarded as an rvalue.

    Now, let's call foo like this:

    int i = 42;
    foo(i);
    
    • i is an lvalue of type int.
    • Because of the special rule for perfect forwarding, when an lvalue of type V is used to deduce T in a parameter of type T &&, V & is used for deduction. Therefore, in our case, T is deduced to be int &.

    Therefore, we specify int & as the template argument to std::forward. Its return type will therefore be "int & &&", which collapses to int &. That's an lvalue, so i is forwarded as an lvalue.

    Summary

    Why this works with templates is when you do std::forward<T>, T is sometimes a reference (when the original is an lvalue) and sometimes not (when the original is an rvalue). std::forward will therefore cast to an lvalue or rvalue reference as appropriate.

    You cannot make this work in the non-template version precisely because you'll have only one type available. Not to mention the fact that setImage(Image&& image) would not accept lvalues at all—an lvalue cannot bind to rvalue references.

    0 讨论(0)
  • 2020-12-04 10:30

    You have to specify the template type in std::forward.

    In this context Image&& image is always an r-value reference and std::forward<Image> will always move so you might as well use std::move.

    Your function accepting an r-value reference cannot accept l-values so it is not equivalent to the first two functions.

    0 讨论(0)
提交回复
热议问题