2

I was trying out a program on universal references by applying std::move and std::forward on them. Until today I assumed that both were the same but in this very program (given below) the output amazed me.

#include <iostream>
#include <string>
#include <utility>
using namespace std;

class X
{
    string take="";
    public:
    template<class T>
    void pass1 (T &&str)   // str is a universal reference as it can bind to anything, both rvalue and lvalue references
    {
        take=move(str);
    }
    template<class T>
    void pass2 (T &&str)
    {
        take=forward<T>(str);
    }
    void show()
    {
        cout<<"take = "<<take<<"\n";
    }
}obj;

int main()
{
    cout<<"using move on universal reference:-\n";
    string str="he is there";
    cout<<"str = "<<str<<'\n';
    obj.pass1(str);
    obj.show();
    if (str.empty())
    cout<<"str is empty\n\n";
    else
    cout<<"str isnt empty\n\n";
    cout<<"using forward on universal reference:-\n";
    str="he was there";
    cout<<"str = "<<str<<'\n';
    obj.pass2(str);
    obj.show();
    if (str.empty())
    cout<<"str is empty\n\n";
    else
    cout<<"str isnt empty\n\n";
    return 0;
}

Output:

using move on universal reference:-
str = he is there
take = he is there
str is empty

using forward on universal reference:-
str = he was there
take = he was there
str isnt empty
*/

My questions are:

  1. Why are the outputs different?
  2. Don't move and forward work similarly? How are they working differently (in context with the above code)?
nwp
  • 9,623
  • 5
  • 38
  • 68
Anwesha
  • 728
  • 1
  • 7
  • 18

4 Answers4

3

They are different, of course. If they were the same, you would only need one.

move constructs rvalue from provided rvalue or lvalue. forward deciphers the actual type of the provided argument.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
SergeyA
  • 61,605
  • 5
  • 78
  • 137
3

T && has been renamed to a forwarding reference.

In your first example you explicitly call std::move so str becomes an r-value reference and it's contents are moved from main to the member in X.

In the second example you use std::forward. calling std::forward on T when T is an r-value reference will forward the r-value reference to operator= and operator=(std::String&&) will be called. If T is an l-value then a l-value reference is passed. Since we have an l-value the operator=(const std::string&) will be called and we copy str instead of move from it.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
3

Understand two thing:

1) 'move' does not guarantee to move

2) 'forward' does not forward a code.

move is an unconditional operator

forward is a conditional operator.

Confused??

please watch this video: https://www.youtube.com/watch?v=BezbcQIuCsY

vishal
  • 2,258
  • 1
  • 18
  • 27
1

Universal references mean that they can bind with apparently anything, both rvalues and lvalues. move & forward donot MOVE ANYTHING LITERALLY and rather perform casts ie they cast the argument to an rvalue. The difference is that move casts unconditionally whereas forward will cast conditionally: it casts to an rvalue only if its argument was initialized with an rvalue. So move always casts whereas forward casts sometimes. Hence the forward in the above case doesn't empty the string as an lvalue (ie str) was passed so it FORWARDED str as an lvalue.

Ankit Acharya
  • 2,833
  • 3
  • 18
  • 29