Understanding object copying when containers are assigned in c++ [closed]
Clash Royale CLAN TAG#URR8PPP
Understanding object copying when containers are assigned in c++ [closed]
I am trying to understand the behavior of object copying and object move when containers are involved. Here is a sample code i wrote.
#include<memory>
#include<iostream>
#include<vector>
using namespace std;
class X
int val;
public:
X(int i):val(i)cout<<"constructor X"<< val <<endl;
X()cout<<"constructor X"<< val <<endl;
X(const X &a) cout<<"copy constructor X"<< val <<endl;
X(X &&a) cout<<"move constructor X"<< val <<endl;
~X()cout<<"Distructor X"<< val << endl;
X& operator = (X a) cout<<"Assignment operator"<<endl; return a;
void do1()cout<<"Do"<<endl;
;
vector<X> container_copy()
vector<X> a1(10);
return a1;
main()
vector<X> b;
vector<X> a = container_copy(); //#1 .
b = a; // #2
cout<<"Done"<<endl;
here is the sample output i got using
constructor X0
constructor X0
constructor X0
copy constructor X0
copy constructor X0
copy constructor X0
Done
Distructor X0
Distructor X0
Distructor X0
Distructor X0
Distructor X0
Distructor X0
Here is my queries.
Why is the move constructor not called for statement marked #1
Why is the copy constructor called instead of assignment operator for #2
I am using following command for compiling under gcc
g++ <file_name>.cpp --std=c++11
g++ <file_name>.cpp --std=c++11
This question appears to be off-topic. The users who voted to close gave this specific reason:
Behaviour of the code is moot, as this will not compile. Post compilable code.
– Neil Butterworth
Aug 10 at 19:47
In addition, if you got rid of all of the bugs, compiling with optimizations on would more than likely give you different results.
– PaulMcKenzie
Aug 10 at 19:49
You will never see any of your constructors called when the vector is returned from function, because it is a vector which is moved, not your objects. Moving a vector effectively means changing a value of a pointer, and original objects remain intact.
– SergeyA
Aug 10 at 19:57
There are several problems in this code.
const X&&
is wrong in a move ctor, use plain X&&
. operator =
must have a return
statement. main
must be declared with a type.– n.m.
Aug 10 at 20:01
const X&&
X&&
operator =
return
main
3 Answers
3
Why is the move constructor not called for statement marked #1
The vector itself isn't moved because of the return value optimisation. The function constructs its return value directly in the caller space, so there's no moving or copying. The vector elements are not moved regardless. Elements are never moved when the vector itself is copy- or move-conctructed.
Why is the copy constructor called instead of assignment operator for #2
Container aasignment works like this: throw away old elements, then copy new elements in. At the second stage the container has no elements to assign to, thus no assignment. Why throw old elements away? For consistency: the number of existing elements may be larger or smaller than the number of new elements, and we don't want complicated implementation and inconsistent results for this operation.
Great to the point answer. Appreciate your help.
– David
Aug 10 at 20:07
This is wrong answer. Even with RVO, OP would NEVER see move constructors called for vector elements when vector is moved.
– SergeyA
Aug 10 at 20:19
@SergeyA good catch, updated the answer.
– n.m.
Aug 10 at 20:32
Taking your two questions in order:
In the first case, your element move-constructor will never be fired in this context, no matter what. Given your output, the function call is undergoing RVO. I.e. a
is what is actually constructed as if it were the vector in the function. Even without RVO, there still wouldn't be a need for element-moving. Rather, you would see something similar to your second case which I describe next
a
In the second case, you asked why there were no assignment operators invoked. Copy construction is invoked because there is nothing to assign to (much less move to), so there won't be element moving going on here either. b
is initially empty, remember? The result of b = a
will simply reserve space for the elements in a
, then then copy-construct each element out of a
to finish off the b
vector. No moves are required (nor wanted), of either your elements nor any vectors. You asked for a copy, and that's what you're getting.
b
b = a
a
a
b
Worth noting, had you asked for a move-assignment of the vector rather than a copy-assignment:
b = std::move(a);
you still would not get element-wise move-construction. The vector itself would be moved. If b
were previously populated, those contents would be destroyed and the result would be b
taking ownership of a
's elements.
b
b
a
In short, there is zero use for a move-constructor for the given code. It will never be called, because it should never be called.
Due to return value optimisation there is probably no copy/move involved in calling container_copy
.
container_copy
If you compile with optimisations turned on the function is almost certainly small enough to be inlined so your main function would become and even return value optimisation is unnecessary:
main()
vector<X> b;
vector<X> a(10); //#1 .
b = a; // #2
cout<<"Done"<<endl;
#2
is calling the copy constructors as it is the vector itself which is being assigned which internally will be doing something like this (roughly, this code isn't optimal but just to give you the idea):
#2
std::vector<X> operator = ( const std::vector<X>& other )
clear();
for (const auto& x : other)
push_back(X(x)); // actually its probably doing a placement new rather than push_back which would result in an additional copy
Shouldn't the compiler be calling the move constructor during the return value optimization?
– David
Aug 10 at 19:57
Inlining is not the root cause of the fact that returning vector from function doesn't call move constructor of vector elements.
– SergeyA
Aug 10 at 19:58
I did see the same behavior when i compile with -g and the function is not inlined
– David
Aug 10 at 19:58
@SergeyA I didn't mean that inlining was the cause, just illustrating the extreme case where the function call disappears completely due to optimisation.
– Alan Birtles
Aug 10 at 20:01
@David the presence or absence of the
-g
switch shouldn't have any affect, it only enables debugging symbols. Optimisation is controlled with -O0
, `-O3' etc.– Alan Birtles
Aug 10 at 20:02
-g
-O0
Your assignment operator invokes undefined behavior. You failed to return a value.
– PaulMcKenzie
Aug 10 at 19:47