user1701840
user1701840

Reputation: 1112

why move constructor is not called

So I am new to move semantic and I am testing out the following code. My understanding is that rvalue will invoke the move constructor and I expected A("123") will cause the move constructor to be called. But when I ran this, the copy constructor is called instead.

#include <string>
#include <iostream>
#include <utility>

class  A
{
    std::string s;
    public:

    A(const std::string& in) : s(in) { std::cout << "ctor!\n";}
    A(const A& o) : s(o.s) { std::cout << "move failed!\n"; }
    A(A&& o) noexcept : s(std::move(o.s)) { }
};

class  B
{
    A d_a;
    public:
    B(const A& a) :d_a(a)
{}
};

int main()
{
    std::cout << "Trying to move A\n";
    B b(A("123")); // move-constructs from rvalue temporary

}

Upvotes: 2

Views: 443

Answers (2)

meguli
meguli

Reputation: 1526

A const lvalue reference binds to anything. Your B has a constructor that takes A by a const lvalue reference. When you pass a temporary A to this constructor, this will eventually lead to some parameter setting that looks like

const A& tmp = temporary_A;

and from then on, your A will be treated as a const lvalue reference which makes a call to copy constructor since that matches the signature. You need to define a constructor in B that takes A by an rvalue reference.

B(A&& a)

As things currently are, you will see a print out like:

Trying to move A

ctor!

move failed!

ctor! is printed when constructing the temporary and since your parameter is taken as const lvalue reference d_a(a) calls the copy constructor. Below, modified code avoids copy.

#include <string>
#include <iostream>
#include <utility>

class  A
{
    std::string s;
    public:

    A(const std::string& in) : s(in) { std::cout << "ctor!\n";}
    A(const A& o) : s(o.s) { std::cout << "move failed!\n"; }
    A(A&& o) noexcept : s(std::move(o.s)) { }
};

class  B
{
    A d_a;
    public:
    B(A&& a) :d_a(std::move(a))
    {}
};

int main()
{
    std::cout << "Trying to move A\n";
    B b(A("123")); // move-constructs from rvalue temporary
}

Upvotes: 2

lubgr
lubgr

Reputation: 38267

The issue is the constructor of B:

B(const A& a) :d_a(a) {}

The function parameter const A& is a const-qualified lvalue reference, which you can't cast to an rvalue. You need to change the constructor to (or add a second one)

B(A&& a) : d_a(std::move(a)) {}

As a side note, you get the correct move semantics for the types in your example for free, if you just define them as

struct  A {
    std::string s;
};

struct B {
     A d_a;
};

with the client code

B b{A{"123"}};

I understand that you wanted to not rely on compiler-generated special member functions for the sake of investigating the move-construction, I just didn't want to omit this shortcut, because this is the setup one should strive for: let the copy and move semantics of your class be automatically assembled by their data members.

Upvotes: 6

Related Questions