why move constructor is not called - c++

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
}

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.

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
}

Related

Copy vs move in std::pair braced initialization

I have the following code and its output printed below. I can't seem to understand why one set of braced initialization results in the move constructor being called, while the other results in the copy constructor. I have somewhat narrowed it down to direct-list-initialization vs copy-list-initialization per https://en.cppreference.com/w/cpp/language/list_initialization
I just can't quite figure out which case my code belongs to. Thanks in advance.
#include <cstdint>
#include <iostream>
using namespace std;
struct Foo {
Foo() {
cout << "create foo\n";
}
~Foo() {
cout << "delete foo\n";
}
Foo(const Foo& f) {
cout << "copy foo\n";
}
Foo(Foo&& f) noexcept {
cout << "move foo\n";
}
Foo& operator=(const Foo& f) = delete;
Foo& operator=(Foo&& f) = delete;
};
int32_t main() {
pair<uint32_t, Foo> f1{0, Foo{}}; // Calls move ctor
cout << "------------------------\n";
pair<uint32_t, Foo> f2{0, {}}; // Calls copy ctor
cout << "------------------------\n";
return 0;
}
This results in
create foo
move foo
delete foo
------------------------
create foo
copy foo
delete foo
------------------------
delete foo
delete foo
Let's take a look at two of the two-argument constructors of pair: [pairs.pair]
EXPLICIT constexpr pair(const T1& x, const T2& y);
template<class U1, class U2> EXPLICIT constexpr pair(U1&& x, U2&& y);
The second constructor uses perfect forwarding, and U2 cannot be deduced from {}. Therefore, the first version is selected when {} is used. When Foo{} is used instead, the argument has type Foo, so U2 is deduced to be Foo, causing the forwarding version to be selected.

Clang-Tidy: Move constructor initializes class member by calling a copy constructor

class Vtx {
private:
int indexPoint;
int radius;
std::string strName;
public:
Vtx(const int p_indexPoint, const int p_radius, std::string p_strName)
: indexPoint(p_indexPoint),
radius(p_radius),
strName(std::move(p_strName)) {
std::cout << "Constructor\n";
}
Vtx(const Vtx& rhs)
: indexPoint(rhs.indexPoint),
radius(rhs.radius),
strName(rhs.strName) {
std::cout << "Copy constructor\n";
}
// (#)
Vtx(const Vtx&& rhs)
: indexPoint(rhs.indexPoint),
radius(rhs.radius),
strName(rhs.strName) {
std::cout << "Move constructor\n";
}
~Vtx() {
std::cout << "Destructor\n";
}
};
For this code, clang-tidy gives me following warning message.
Move constructor initializes class member by calling a copy constructor
I can't understand when the copy constructor is called inside of move constructor.(#) Although compilation works well, but I want to remove this warning. How to I fix it?
You forget to move the members (can be omitted for built-in type where move is copy anyway)
and also moving from const object is generally a copy so you have to change to
Vtx(Vtx&& rhs)
: indexPoint(rhs.indexPoint),
radius(rhs.radius),
strName(std::move(rhs.strName)) {
std::cout << "Move constructor\n";
}
Note that, in your case, except for message (you can move that code in dedicated class to ease Vtx implementation BTW), default implementation would be ok:
Vtx(Vtx&& rhs) = default;

Why isn't move constructor invoked?

Here is the code: http://coliru.stacked-crooked.com/a/f7731b48747c61a9
#include <iostream>
struct A{
A(const A&){ std::cout << "A(const A&)" << std::endl;}
A(const A&&){ std::cout << "A(const A&&)" << std::endl;}
A(){ }
};
A foo(){
return *new A;
}
int main()
{
A a;
A c(foo());
}
Since, I passed to the c's constructor argument a temporary object, I expected the move constructor to be called. But the copy constructor was. Why?
Since foo returns a non-constant rvalue, it cannot bind to the const-rvalue reference. The copy constructor is the only remaining viable overload.
A constructor that would be a better overload than the copy constructor would be:
A(A&&);
Additionally, either constructor (copy or move) may be elided under the rules of copy elision (e.g. see here or here.)

What is copy/move constructor choosing rule in C++? When does move-to-copy fallback happen?

The first example:
#include <iostream>
#include <memory>
using namespace std;
struct A {
unique_ptr<int> ref;
A(const A&) = delete;
A(A&&) = default;
A(const int i) : ref(new int(i)) { }
~A() = default;
};
int main()
{
A a[2] = { 0, 1 };
return 0;
}
It works perfectly. So here the MOVE constructor is used.
Let's remove the move constructor and add a copy one:
#include <iostream>
#include <memory>
using namespace std;
struct A {
unique_ptr<int> ref;
A(const A&a)
: ref( a.ref.get() ? new int(*a.ref) : nullptr )
{ }
A(A&&) = delete;
A(const int i) : ref(new int(i)) { }
~A() = default;
};
int main()
{
A a[2] = { 0, 1 };
return 0;
}
Now the compilation falls with the error "use of deleted function ‘A::A(A&&)’"
So the MOVE constructor is REQUIRED and there is no fall back to a COPY constructor.
Now let's remove both copy- and move-constructors:
#include <iostream>
#include <memory>
using namespace std;
struct A {
unique_ptr<int> ref;
A(const int i) : ref(new int(i)) { }
~A() = default;
};
int main()
{
A a[2] = { 0, 1 };
return 0;
}
And it falls with "use of deleted function ‘A::A(const A&)’" compilation error. Now it REQUIRES a COPY constructor!
So there was a fallback (?) from the move constructor to the copy constructor.
Why? Does anyone have any idea how does it conform to the C++ standard and what actually is the rule of choosing among copy/move constructors?
There is no "fallback". It is called overload resolution. If there are more than one possible candidate in overload resolution then the best match is chosen, according to a complicated set of rules which you can find by reading the C++ standard or a draft of it.
Here is an example without constructors.
class X { };
void func(X &&) { cout << "move\n"; } // 1
void func(X const &) { cout << "copy\n"; } // 2
int main()
{
func( X{} );
}
As-is: prints "move"
Comment out "1": prints "copy"
Comment out "2": prints "move"
Comment out "1" and "2": fails to compile
In overload resolution, binding rvalue to rvalue has higher preference than lvalue to rvalue.
Here is a very similar example:
void func(int) { cout << "int\n"; } // 1
void func(long) { cout << "long\n"; } // 2
int main()
{
func(1);
}
As-is: prints "int"
Comment out "1": prints "long"
Comment out "2": prints "int"
Comment out "1" and "2": fails to compile
In overload resolution, an exact match is preferred to a conversion.
In your three examples on this thread we have:
1: Two candidate functions; rvalue prefers rvalue (as in my first example)
A(const A&);
A(A&&); // chosen
2: Two candidate functions; rvalue prefers rvalue (as in my first example)
A(const A&);
A(A&&); // chosen
3: One candidate function; no contest
A(const A&); // implicitly declared, chosen
As explained earlier, there is no implicit declaration of A(A&&) in case 3 because you have a destructor.
For overload resolution it does not matter whether the function body exists or not, it is whether the function is declared (either explicitly or implicitly).
The function that it's going to use is chosen before it checks if it is deleted or not. In the case that the copy constructor was available and the move constructor was deleted, the move constructor was still the best choice out of the two. Then it sees that it's deleted and gives you an error.
If you had the same example but actually removed the move constructor, rather than making it deleted, you'll see that it compiles fine and does fall back to use the copy constructor:
#include <iostream>
#include <memory>
using namespace std;
struct A {
unique_ptr<int> ref;
A(const A&a)
: ref( a.ref.get() ? new int(*a.ref) : nullptr )
{ }
A(const int i) : ref(new int(i)) { }
~A() = default;
};
int main()
{
A a[2] = { 0, 1 };
return 0;
}
This class does not have a move constructor declared for it at all (not even implicitly), therefore it cannot be chosen.

Why constructor does not work in some cases?

I have a class definition. And I am confused about some constructor behaviour. Following is the code.
#include <iostream>
#include <cstdlib>
using namespace std;
class A
{
int i;
public:
void seti(int v) {cout << "Setting i\n"; i=v;}
void print() {cout << "i = " << i << endl;}
A(){};
A(int v) {cout << "Setting i in par-ctor to " << v << "\n"; i=v;}
A(A& o) {cout << "In copy ctor, changing i to " << o.i << "\n";i=o.i;}
A& operator=(A o) {cout << "In assignment op\n"; this->i = o.i; return(*this);}
};
int main()
{
A o1;
A o2(2);
A o3 = A(4);
A o4 = 35;
return(0);
}
I want to know why this code does not compile unless
a) The defined copy constructor is commented, or
b) The defined copy constructor has a 'const' qualifier as in A& A(const A& o) or
c) Object initializations for both o3 and o4 are removed.
Regarding (c) in which the invocation of constructor A(int) is expected,
How does the actual copy constructor definition (the one without const) conflict with the parametrized (with int) constructor ?
First look here:
A o3 = A(4);
The A(4) creates a temporary object. This expression is an rvalue. An rvalue cannot bind to a non-const lvalue reference like A&, so the copy constructor cannot be chosen. A better copy constructor declaration has a const A&, so that it can be constructed from rvalues too. That's why your (b) fix works.
The same problem manifests here:
A o4 = 35;
In copy-initialisation (with an =), a temporary of the object is constructed and then copied into the object you're declaring. So this is equivalent to:
A o4 = A(35);
And therefore exhibits the same problem as above.