std::move Vs std::forward
Asked Answered
T

1

8

This seems to be most relavant question already asked.

Whats the difference between std::move and std::forward

But each answer is different and applies and says slightly different things. So I am confused.

I have the following situation.

  • Copy item into container
    The Copy item is C++03 so I understand that quite well.
  • Construct item into container
    The Construct item into container I believe uses perfect forwarding correctly to forward the arguments through two functions to the constructor of T in emplaceBackInternal() (Please say otherwise if I am wrong).
  • Move item into container
    My problem seems to be understanding the moving an item into the container.

The Code:

template<typename T>
class Container
{
    std::size_t length;
    T*          buffer;

public:
    void push_back(T const& value)
    {
         resizeIfRequired();
         pushBackInternal(value);
    }
    template<typename... Args>
    void emplace_back(Args&&... args)
    {
         resizeIfRequired();
         emplaceBackInternal(std::forward<T>(arg)...);
    }
    void push_back(T&& value)
    {
         resizeIfRequired();
         // Is this forward correct or should it be move
         moveBackInternal(std::forward<T>(value));
    }
private:
    void pushBackInternal(T const& value)
    {
         // Copy construct object into buffer;
         new (buffer + length) T(value);
         ++length;
    }
    template<typename... Args)
    void emplaceBackInternal(Args&&... args)
    {
         // Construct object into buffer using arguments;
         new (buffer + length) T(std::forward<T>(args)...);
         ++length;
    }
    void moveBackInternal(T&& value)
    {
         // Move construct object into buffer;
         // Is this forward correct or should it be move
         new (buffer + length) T(std::forward<T>(value));
         ++length;
    }
};

I include all three here to compare the three functions with the answers provided in the previously mentioned answer. The main reason is that move and construct looks so similar that it feels like they should be the same.

Answer @Potatoswatter Score 67

std::forward has a single use case: to cast a templated function parameter

By this definition I should be using std::move inside push_back(T&& value) and moveBackInternal(T&& value) as the value is not a template parameter for the function.

Answer @Howard Hinnant Score 38

If Y is an lvalue reference, the result will be an lvalue expression. If Y is not an lvalue reference, the result will be an rvalue (xvalue to be precise) expression.

Seems by this definition I can use either std::move or std::forward.

Answer @Bo Persson Score 11

std::forward is used to forward a parameter exactly the way it was passed to a function.

Seems to say that std::forward is acceptable (though if I follow the link in the answer all the examples use templated functions).

Trembly answered 21/3, 2016 at 21:54 Comment(3)
Er... what's your question that wasn't answered by the linked answers?Turnsole
Er... which answer is correct!. Each one specifies a different result.Trembly
They're all correct. Two just say the intent of std::forward is for forwarding references and the third additionally spends some time explain what std::forward is.Turnsole
T
9

In this case:

void push_back(T&& value)
{
     resizeIfRequired();
     moveBackInternal(std::forward<T>(value));  // (1)             
     moveBackInternal(std::move(value));        // (2) 

}

std::forward<T>(value) and std::move(value) are identical in this scenario (it doesn't matter between (1) and (2)... so use (2)).

move is an unconditional cast to xvalue. That line gives you an expression of type T&& that's an rvalue, always.

forward is a conditional cast. If T is an lvalue reference type, it yields an lvalue. Otherwise (if it's either not a reference type or an rvalue reference type), it yields an rvalue. In our case, T is not a reference type - so we get an rvalue.

Either way, we end up at the same point - we call moveBackInternal with value cast as an rvalue. Just move() is a simpler way of getting there. forward<T> works, but it's unnecessary.

Turnsole answered 21/3, 2016 at 22:3 Comment(3)
Its at times like this I wish I was in an office with you (or the author of a post) and could have a frank discussion with a whiteboard as to the better technique. -- If I make the assumption that at all the work done by both std::move and std::forward is compile time work. Then would not solution (1) be better because it makes the code look identical to emplace_back(). Why do I need two different techniques when I could have one technique that worked in all situations? Less differences means easier to maintain code?Trembly
@LokiAstari Seems primarily opinion based. I find it more straightforward to use move() when we're moving unconditionally. The push_back() code only ever moves, so why use the more complicated cast? Ultimately, I find there's something fundamentally... off... with forwarding references. I don't know how it could be done better - and for better or for worse, this is what we got.Turnsole
Thanks for all the input. To be honest I am still trying to form my opinion and just trying to play devils advocate here. :-)Trembly

© 2022 - 2024 — McMap. All rights reserved.