Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compiler not generating move constructors

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;
    }
like image 226
madu Avatar asked Nov 14 '18 02:11

madu


1 Answers

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 str using move semantics. str is 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.

like image 78
songyuanyao Avatar answered Oct 19 '22 10:10

songyuanyao