Reputation: 1112
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
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
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