0

I am trying to understand move constructor and written following code

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

class mystring
{
    string s;
public:
    mystring(const string& x):
    s(x)
    {

    }
    mystring(const mystring& x)

    {
        cout<<"Copy called"<<endl;
        s = x.s;
    }


    mystring(const mystring&& x)
    {
        cout<<"Move Called"<<endl;
        s = x.s;
    }

    mystring& operator+(const mystring& x)
    {
        cout<<"+ operator"<<endl;
        s = s+x.s;
        return *this;
    }
};

int main()
{
    string a = "Hello ";
    string b = "World ";
    mystring a1(a);
    mystring b1(b);
    mystring c = mystring(a1+b1);
}

I am expecting call of move constructor on the result rValue of a1+b1 but i am seeing only copy constructor is being called. Am i missing something?

gcc --version
gcc (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0

Edited after HolyBlackCat's answer:

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

class mystring
{
    string s;
public:
    explicit mystring(const string& x):
    s(x)
    {
        cout<<"ctor called"<<endl;
    }
    mystring(const mystring& x)

    {
        cout<<"Copy called"<<endl;
        s = x.s;
    }


    mystring(mystring&& x)
    {
        cout<<"Move Called"<<endl;
        s = std::move(x.s);
    }

    mystring operator+(const mystring& x) const
    {
        cout<<"+ operator"<<endl;
        return mystring(s+x.s);
    }
};

int main()
{
    string a = "Hello ";
    string b = "World ";
    mystring a1(a);
    mystring b1(b);
    mystring c(a1+b1) ;
} 

The move constructor is still not being called:

ctor called
ctor called
+ operator
ctor called
HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
SaurabhS
  • 633
  • 1
  • 7
  • 18

3 Answers3

1

There is no reason for the move constructor to be called, since your operator+ doesn't return a temporary.


It's a bad idea to modify the left operand in operator+. Nobody expects a + b to modify a.

You should rewrite it as mystring operator+(const mystring& x) const. This way it does return a temporary, and your move constructor should be called (unless the compiler optimizes it away).

Also, usually the parameter of a move constructor/assignment should be a non-const rvalue reference. You need to be able to modify the parameter to move resources from it.

Also, note that rvalue references are lvalues. It might sound weird, but the point is that without std::move, s = x.s will copy the string even if x is std::string &&.

In the end, the move constructor should look like this:

mystring(mystring &&x)
{
    cout << "Move Called" << endl;
    s = std::move(x.s);
}

Or even better, like this:

mystring(mystring &&x) : s(std::move(x.s))
{
    cout << "Move Called" << endl;
}
HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
0

To move something, it needs to be modified. You are accepting a const mystring&&. A const mystring& can already bind to both lvalue and rvalue since it's const and can't be modified.

Your move constructor should be taking a none const rvalue reference, mystring&& x.

The second issue here is that even if x here is of type rvalue reference, x is still named and is considered an lvalue inside your move constructor.

To actually move the value we need to cast it using std::move.

mystring(mystring&& x)
{
    cout<<"Move Called"<<endl;
    s = std::move(x.s);
}
super
  • 12,335
  • 2
  • 19
  • 29
0

Your code doesn't invoke any move operations, therefore the move constructor is not called.

In the second code: Since C++17, mystring c(a1+b1); it means that the result object of a1+b1 is c. It does not mean that there is a temporary object moved into c.

Prior to C++17 there might have been a temporary object and it was up to the compiler whether to make one (and therefore, whether a move constructor would be called).

M.M
  • 138,810
  • 21
  • 208
  • 365