Are there any use cases for std::forward with a prvalue?

前端 未结 3 1175
醉梦人生
醉梦人生 2021-02-01 17:46

The most common usage of std::forward is to, well, perfect forward a forwarding (universal) reference, like

template
void f(T&         


        
3条回答
  •  耶瑟儿~
    2021-02-01 18:12

    This answer is for answering comment by @vsoftco

    @DarioOO thanks for the link. Can you maybe write a succinct answer? From your example it's still not clear for me why does std::forward need to be also defined for rvalues

    In short:

    Because without a rvalue specialization the following code would not compile

    #include 
    #include 
    using namespace std;
    
    class Library
    {
        vector b;
    public:
        // hi! only rvalue here :)
        Library( vector&& a):b(std::move(a)){
    
        }
    };
    
    int main() 
    {
        vector v;
        v.push_back(1);
        A a( forward>(v));
        return 0;
    }
    

    however I can't resist to type more so here's also the not succint version of the answer.

    Long version:

    You need to move v because the class Library has no constructor accepting lvalue to it, but only a rvalue reference. Without perfect forwarding we would end up in a undesired behaviour:

    wrapping functions would incurr high performance penality when passing heavy objects.

    with move semantics we make sure that move constructor is used IF POSSIBLE. In the above example if we remove std::forward the code will not compile.

    So what is actually doing forward? moving the element without our consensus? Nope!

    It is just creating a copy of the vector and moving it. How can we be sure about that? Simply try to access the element.

    vector v;
    v.push_back(1);
    A a( forward>(v)); //what happens here? make a copy and move
    std::cout<

    if you instead move that element

    vector v;
    v.push_back(1);
    A a( std::move(v)); //what happens here? just moved
    std::cout<

    So that overload is needed to make possible a implicit conversion that is still safe, but not possible without the overload.

    Infact the following code will just not compile:

    vector v;
    v.push_back(1);
    A a( v); //try to copy, but not find a lvalue constructor
    

    Real use case:

    You may argue that forwarding arguments may create useless copies and hence hide a possible performance hit, yes, that's actually true, but consider real use cases:

    template< typename Impl, typename... SmartPointers>
    static std::shared_ptr 
        instancesFactoryFunction( priv::Context * ctx){
            return std::static_pointer_cast( std::make_shared(
    
                    std::forward< typename SmartPointers::pointerType>( 
                SmartPointers::resolve(ctx))... 
                )           );
    }
    

    Code was taken from my framework (line 80): Infectorpp 2

    In that case arguments are forwarded from a function call. SmartPointers::resolve's returned values are correctly moved regardless of the fact that constructor of Impl accept rvalue or lvalue (so no compile errors and those get moved anyway).

    Basically you can use std::foward in any case in wich you want to make code simpler and more readable but you have to keep in mind 2 points

    • extra compile time (not so much in reality)
    • may cause unwanted copies (when you do not explicitly move something into something that require a rvalue)

    If used with care is a powerfull tool.

提交回复
热议问题