Why isn't move constructor invoked? - c++

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.)

Related

Why userdefined move constructor is not called in below example?

I am expecting that after using std::move(any_object) we are converting that object in rvalue reference and due to this it should call my userdefined move constructor. but it is not working like this.Could anyone tell whats happening?
#include <iostream>
#include <memory>
struct Demo {
int Value;
Demo():Value(10) { std::cout << "Demo's default constructor called\n";}
Demo(Demo& tempObj) { std::cout << "Demo non-const copy constructor called\n";}
Demo(const Demo& tempObj) { std::cout << "Demo const copy constructor called\n"; }
Demo(Demo&& tempObj) { std::cout << "Demo move constructor called\n"; }
Demo(const Demo&& tempObj) { std::cout << "Demo const move constructor called\n"; }
Demo& operator= (const Demo& tempObj){ std::cout << "Demo copy assignment operator called ";}
Demo& operator= (Demo&& tempObj){ std::cout << "Demo move assignment operator called";}
~Demo(){ std::cout << "Demo's destructor called\n";}
};
void fun(Demo&& tempObj)
{
tempObj.Value = 20;
}
int main()
{
Demo demo;
fun(std::move(demo));
}
//output:
Demo's default constructor called
Demo's destructor called
Why userdefined move constructor is not called in below example?
Because you're only binding an rvalue reference to an xvalue and not actually constructing an instance of type Demo. That is, a move constructor will be called when you actually construct an instance of type Demo using a rvalue as shown below for example:
//-----------v-------->removed && from here
void fun(Demo tempObj)
{
tempObj.Value = 20;
}
fun(std::move(demo)); //now this uses move constructor
Additionally, you missing return statement in your assignement operators.

Is it possible to provide a constuctor with copy elision for member initialization?

I'm testing different modes for initializing class members with following small code:
struct S {
S() { std::cout << "ctor\n"; }
S(const S&) { std::cout << "cc\n"; }
S(S&&) noexcept{ std::cout << "mc\n"; }
S& operator=(const S&) { std::cout << "ca\n"; return *this; }
S& operator=(S&&) noexcept{ std::cout << "ma\n"; return *this; }
~S() { std::cout << "dtor\n"; }
};
struct P1 {
S s_;
};
struct P2 {
P2(const S& s) : s_(s) {}
S s_;
};
struct P3 {
P3(const S& s) : s_(s) {}
P3(S&& s) : s_(std::move(s)) {}
S s_;
};
int main() {
S s;
std::cout << "------\n";
{
P1 p{s}; // cc
}
std::cout << "---\n";
{
P1 p{S{}}; // ctor = copy elision
}
std::cout << "------\n";
{
P2 p{s}; // cc
}
std::cout << "---\n";
{
P2 p{S{}}; // ctor + cc
}
std::cout << "------\n";
{
P3 p{s}; // cc
}
std::cout << "---\n";
{
P3 p{S{}}; // ctor + mc
}
std::cout << "------\n";
}
As you see in comments, only in case of aggregate-initialization of P1{S{}} copy elision happens and our class is initialized without any copy/move constructor calls. I wonder if it is possible to provide a constructor which initialize members directly like aggregate initializer. Any idea?
Update:
I wonder if I have understood standard incorrectly, but from my understanding, something strange happens here:
For initializer list we have:
class-or-identifier ( expression-list(optional) ):
Initializes the base or member named by class-or-identifier using direct initialization or, if expression-list is empty, value-initialization
For direct initialization we have:
If T is a class type, if the initializer is a prvalue expression whose type is the same class as T (ignoring cv-qualification), the initializer expression itself, rather than a temporary materialized from it, is used to initialize the destination object. (copy elision)
So from this, I thought that for a initializer list like s_(std::move(s)) copy elision should happen, isn't it?
I wonder if it is possible to provide a constructor which initialize members directly like aggregate initializer.
Certainly. Write a constructor that doesn't accept an argument of the member type, but rather accepts arguments that are forwarded to the constructor of the member. In your case, the member type is default constructible, so you don't need to forward any arguments:
struct P4 {
P4(): s() {}
S s;
};

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
}
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
}

Destructor called one more time than I expected

Here is my min ex:
#include <iostream>
#include <vector>
class A {
public:
A() { std::cout << "Constructor\n"; }
~A() { std::cout << "Destructor\n"; }
};
class B {
public:
B() { v.push_back(A()); v.push_back(A()); }
private:
std::vector<A> v;
};
int main() {
B b;
return 0;
}
So, I got this output:
Constructor // first push back
Destructor // copy destroyed
Constructor // second push back
Destructor // copy destroyed
Destructor // ???????????????
// B object goes out of scope and its vector too...
Destructor // v[0] destructor called
Destructor // v[1] destructor called
Can someone shed some light please?
After following Dave's comment:
Constructor
Copy constructor
Destructor
Constructor
Copy constructor
Copy constructor
Destructor
Destructor
Destructor
Destructor
Adding in an overloaded copy constructor and some indicator of which object is being acted upon sheds some light on the situation:
#include <iostream>
#include <vector>
class A {
public:
A(unsigned i): i(i) { std::cout << "Constructor " << i << std::endl; }
A(const A& a) : i(a.i) { std::cout << "Copy constructor " << i << std::endl; }
~A() { std::cout << "Destructor " << i << std::endl; }
unsigned i;
};
class B {
public:
B() { v.push_back(A(0)); v.push_back(A(1)); }
private:
std::vector<A> v;
};
int main() {
B b;
return 0;
}
On the first push, we make a copy and destroy the temporary. On the second push, we make a copy, then copy the first object, before destroying the first object and the temporary. Finally, we destroy both objects.
I'd guess that the std::vector was first allocated with a capacity of 1, so the second push forced a re-allocation? If I force a larger initial capacity (by calling v.reserve(5) before the first push), then the extra copy disappears.
You are not tracking construction of As using the default copy constructor. If you add a copy constructor and a message in it, the number of calls to constructors should match the number of calls to the destructor.
Modifying declaration of class A as follows,
class A
{
public:
A() { std::cout << "Constructor " << this << std::endl; }
A(const A&) { std::cout << "Copy Constructor " << this << std::endl; }
~A() { std::cout << "Destructor " << this << std::endl; }
};
running the program at http://coliru.stacked-crooked.com/
gives output:
Constructor 0x7fffecb8dc0f
Copy Constructor 0x1efdc20
Destructor 0x7fffecb8dc0f
Constructor 0x7fffecb8dc0e
Copy Constructor 0x1efdc41
Copy Constructor 0x1efdc40
Destructor 0x1efdc20
Destructor 0x7fffecb8dc0e
Destructor 0x1efdc40
Destructor 0x1efdc41
which clearly shows the construction and destruction of objects , both on stack and on heap (0x1efdc20, 0x1efdc40, and 0x1efdc41 are locations of the objects allocated by the vector).

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.