I am trying to understand the move semantics are looking in to compiler generated move constructors (copy and assignment).
In Modern Effective C++, Scott Meyers says in Item #17 that if no explicit copy constructors are declared, the the compiler will generate move constructors, which will do member-wise move for non-static members.
To confirm this, I am trying below code:
#include <iostream>
#include <string>
using namespace std;
class A
{
private:
    std::string str;
public:
    A() : str("Init string")
    {
        cout << "Default constructor" << endl;
    }
    A(std::string _str) : str(_str)
    {
        cout << "Constructor with string" << endl;
    }
    std::string getString()
    {
        return str;
    }
};
int main() {
    A obj1;
    A obj2("Obj2 string");
    cout << endl;
    cout << "obj1: " << obj1.getString() << endl;
    cout << "obj2: " << obj2.getString() << endl;
    obj1 = std::move(obj2);
    cout << endl;
    cout << "obj1: " << obj1.getString() << endl;
    cout << "obj2: " << obj2.getString() << endl;
    return 0;
}
The output is:
Default constructor
Constructor with string
obj1: Init string
obj2: Obj2 string
obj1: Obj2 string
obj2: Obj2 string
But I expected it to be:
Default constructor
Constructor with string
obj1: Init string
obj2: Obj2 string
obj1: Obj2 string
obj2: 
Because obj2.str would have been moved and now has an empty string.
What is the reason the compiler is not generating a move assignment constructor and invoking the copy assignment operator?
EDIT: Implementing the move assignment operator as below gives the expected output (i.e. empty string after calling std::move)
A& operator=(A&& obj)
    {
        cout << "Move assignment operator" << endl;
        str = std::move(obj.str);
        return *this;
    }
                Firstly, obj1 = std::move(obj2); invokes assignment operator, so it has nothing to do with constructors.
Yes, The compiler generates a move assignment operator for A, which perform member-wise move operation, including data member str. The problem is that after move operation str is left in valid, but unspecified state. Also see std::basic_string::operator=.
Replaces the contents with those of
strusing move semantics.stris in a valid but unspecified state afterwards.
I think you might observe the same result with only std::string, e.g.
std::string str1 = "Init string";
std::string str2 = "Obj2 string";
str1 = std::move(str2);
std::cout << str2;
LIVE with clang, just for reference; it gives the result as you expected but still remember the result is unspecified.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With