Reputation: 6794
In the following code, I would expect that the copy constructor of A
is never called, as the items should be created directly at the vector with emplace_back
and then the total result should be return-value-optimized.
This seems to be the case. However, every time I append to the vector, all previous items are being copied as a result of the emplace_back
call. Why is this happening?
#include <iostream>
#include <vector>
#include <string>
using namespace std;
static int _id = 0;
class A{
public:
A(): name(_id++){
cout << "Created" << this->name << endl;
}
A(const A& other): name(other.name){
cout << "Copied" << this->name << endl;
}
A(const A&& other): name(other.name){
cout << "Moved" << this->name << endl;
}
~A(){
cout << "Deleted"<< this->name <<endl;
}
private:
int name;
};
vector<A> f2(){
cout << "Entering f2" << endl;
auto ret = vector<A>();
for (int i = 0; i < 3; i++){
//auto obj = A();
cout << "Adding obj" << endl;
ret.emplace_back();
cout << "Added obj" << endl;
}
cout << "Returning" << endl;
return ret;
}
int main()
{
{
auto c = f2();
cout << "Exiting stack" << endl;
}
return 0;
}
I compile it with gcc 4.8.4 using any of:
gcc main.cpp -std=c++11
gcc main.cpp -std=c++11 -O3
The output is:
Entering f2
Adding obj
Created0
Added obj
Adding obj
Created1
Copied0
Deleted0
Added obj
Adding obj
Created2
Copied0
Copied1
Deleted0
Deleted1
Added obj
Returning
Exiting stack
Deleted0
Deleted1
Deleted2
And I would expect to not have copies, something like:
Entering f2
Adding obj
Created0
Added obj
Adding obj
Created1
Added obj
Adding obj
Created2
Added obj
Returning
Exiting stack
Deleted0
Deleted1
Deleted2
Changing emplace_back
with push_back
doesn't cure this, and adds additional moves.
Upvotes: 2
Views: 151
Reputation: 455
Also, if you mark your move constructor noexcept, then when the vector grows, it will move rather than copying.
See also How to enforce move semantics when a vector grows?
Upvotes: 3
Reputation: 33579
Vector will re-allocate its memory block when it runs out of storage space, and copy the old objects into their new places. You can significantly reduce the number of such re-allocations by calling vector.reserve(<expected number of items>);
Also worth noting is that vector guarantees amortized constant push_back
complexity. It means that as you keep adding items, reallocations will occur less and less often, so it's usually not a concern.
Or, if you just need some sort of a container and don't need it to be allocated in memory contiguously, consider using a different container. Like std::deque
.
Upvotes: 4