Double move on same object is copying from left to right?

3.3k Views Asked by At

I am just beginner in move operation in c++11, so playing with it. But found something which i am not able to understand.

#include <iostream>
using namespace std;

class A{
    public:
        A(){cout << "default ctor" << endl;}
        A(const string& str):_str{str}{cout << "parameter ctor" << endl;}
        A(A&& obj):_str{std::move(obj._str)}{cout << "move ctor" << endl;}
        A& operator =(A&& rhs){_str = std::move(rhs._str);cout << "move assignment operation" << endl; return *this;}
        void print(){cout << _str << endl;}
    private:
        string _str;
};

int main(){
    A a("rupesh yadav"); // parameter ctor
    A b(std::move(a));   // move ctor

    cout << "print a: ";
    a.print();           // NOT printing  --> CORRECT!!
    cout << "print b: ";
    b.print();           // printing      --> CORRECT!!

    b = std::move(a);    // i don't know may be silly but still lets do it WHY NOT!!!, could be just mistake??

    cout << "print a: "; 
    a.print();           // printing      --> WRONG!! 
    cout << "print b: "; 
    b.print();           // NOT printing  --> WRONG!!
}

I was expecting that b = std::move(a) operation would behave something different because i am applying move on object a second time but it is copying left side object b to right hand side object a, this part i don't understand.

Or i have done something wrong in programming. Please help if i am doing something wrong in move operation.

EDIT: I know this is undefined behavior. My doubt is if i will do it again then it is copying from object a to object b, and if again i will do the same thing then will copy object b to object a?

Hence it is copying form left to right and right to left why?

2

There are 2 best solutions below

0
On

Nothing prevents you from moving from an object twice. Standard library objects are required to be left in a "valid, but unspecified" state when moved from. The only condition is that "the object's invariants are met and operations on the object behave as specified for its type" 17.3.28. As an example, the move constructor for std::string says:

basic_string(const basic_string& str);

basic_string(basic_string&& str) noexcept;

2 Effects: Constructs an object of class basic_string as indicated in Table [tab:strings.ctr.cpy]. In the second form, str is left in a valid state with an unspecified value.

The move constructor has no preconditions and moving from it again would not violate its invariants. Furthermore, since it's clearly outlined in the standard, by definition it's not undefined behavior. You could argue by omission that violating the invariants of std::string is undefined behavior however, but that leads us to:

Is calling operator<< on a std::string disallowed? The description from cppreference says:

Behaves as a FormattedOutputFunction. After constructing and checking the sentry object, determines the output format padding as follows:

  • If str.size() is not less than os.width(), uses the range [str.begin(), str.end()) as-is

  • Otherwise, if (os.flags() & ios_base::adjustfield) == ios_base::left, places os.width()-str.size() copies of the os.fill() character after the character sequence

  • Otherwise, places os.width()-str.size() copies of the os.fill() character before the character sequence

size(), begin() and end() have no preconditions so the code is perfectly safe.

10
On

You can't move from the same object twice.

After you first moved a into b, a had a "valid but unspecified state" (can't remember the exact terminology). Then, you attempted to move a into b again! Now all heck has broken loose. (I suspect that, internally, the data pointers have just been swapped around.)

Simply don't do this. I see no reason to want to.