6

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?

Rupesh Yadav.
  • 894
  • 2
  • 10
  • 24

2 Answers2

6

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.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • You're thinking of [17.3.28](http://eel.is/c++draft/defns.valid): `an object state that is not specified except that the object's invariants are met and operations on the object behave as specified for its type`, also [17.6.5.15](http://eel.is/c++draft/lib.types.movedfrom): `Objects of types defined in the C++ standard library may be moved from ([class.copy]). Move operations may be explicitly specified or implicitly generated. Unless otherwise specified, such moved-from objects shall be placed in a valid but unspecified state.` – uh oh somebody needs a pupper May 09 '16 at 10:02
  • The rule only applies to the standard library. This implies that it's a constraint on library developers but doesn't make it safe to do so in general. – uh oh somebody needs a pupper May 09 '16 at 10:03
  • @user6292850: Sounds about right. And `std::string` is part of the standard library. – Lightness Races in Orbit May 09 '16 at 10:04
  • @LightnessRacesinOrbit i know this is undefined behavior but it is behaving like defined :) . – Rupesh Yadav. May 09 '16 at 10:05
  • @RupeshYadav.: Doing what you dream about is one possible symptom of undefined behaviour. Doesn't make it defined. – Lightness Races in Orbit May 09 '16 at 10:07
  • What I mean is "leave in a valid but unspecified state" is something required for the implementator. The language doesn't prevent you from moving from a string object twice, but prevents the users from calling operations that have preconditions (i.e, `front()` requires `empty()` should be false) – uh oh somebody needs a pupper May 09 '16 at 10:08
  • @user6292850: Oh I meant logically. Once you've moved Data from X to Y, Data is no longer in X so you can't move it from X to Y again. I don't really see the purpose of what the OP's asking tbh. – Lightness Races in Orbit May 09 '16 at 10:12
  • @user6292850 say someone wrongly moved twice then what would be the behavior, at least my called by object should not be changed.. my concern is here it is changing my called by object which is undesired.. – Rupesh Yadav. May 09 '16 at 11:17
  • 2
    @RupeshYadav: Moving (potentially) modifies both the source and the target and the source is modified in an unspecified manner. So you really shouldn't expect anything about the CONTENT of a moved from object(irrespective of how often you move from it) – MikeMB May 09 '16 at 11:36
  • @MikeMB this sounds reasonable. – Rupesh Yadav. May 10 '16 at 03:42
4

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.