Reputation: 1029
I am really confused about std::vector::emplace_back
. I run the following code:
struct Thing {
explicit Thing(std::string name) : name_{std::move(name)} {
std::cout << "Constructed a thing called " << name_ << std::endl;
}
~Thing() {
std::cout << "Deconstructed a thing called " << name_ << std::endl;
};
std::string name_;
};
int main() {
std::vector<Thing> things{Thing("A")};
std::cout << "Size: " << things.size() << std::endl;
things.emplace_back("B");
std::cout << "Size: " << things.size() << std::endl;
}
and get this output:
Constructed a thing called A
Deconstructed a thing called A
Size: 1
Constructed a thing called B
Deconstructed a thing called A
Size: 2
Deconstructed a thing called A
Deconstructed a thing called B
Why on earth does things.emplace_back("B")
invoke the deconstructor of the thing called A?
Upvotes: 1
Views: 106
Reputation: 40791
What's happening is that when you add B
to the vector, the vector probably only had capacity for 1 element, so had to move it's existing element into another vector (destructing the one in the original storage)
Let's add some more logging to see what happens:
#include <iostream>
#include <string>
#include <vector>
struct Thing {
explicit Thing(std::string name) : name_{std::move(name)} {
std::cout << "Constructed a thing called " << name_ << " at " << this << std::endl;
}
Thing(Thing&& t) noexcept : name_{std::move(t.name_)} {
std::cout << "Moved a thing called " << name_ << " from " << &t << " to " << this << std::endl;
}
Thing(const Thing& t) : name_{t.name_} {
std::cout << "Copied a thing called " << name_ << " from " << &t << " to " << this << std::endl;
}
~Thing() {
std::cout << "Deconstructed a thing called " << name_ << " at " << this << std::endl;
};
std::string name_;
};
int main() {
std::vector<Thing> things{Thing("A")};
std::cout << "Size: " << things.size() << " Capacity: " << things.capacity() << std::endl;
things.emplace_back("B");
std::cout << "Size: " << things.size() << " Capacity: " << things.capacity() << std::endl;
}
Example output:
Constructed a thing called A at 0x1111
Copied a thing called A from 0x1111 to 0x2222
Deconstructed a thing called A at 0x1111
Size: 1 Capacity: 1
Constructed a thing called B at 0x3333
Moved a thing called A from 0x2222 to 0x4444
Deconstructed a thing called A at 0x2222
Size: 2 Capacity: 2
Deconstructed a thing called A at 0x4444
Deconstructed a thing called B at 0x3333
Upvotes: 1
Reputation: 22152
std::vector
stores the objects continuously in an allocated memory block. When that block's size becomes too small to add new elements into it, it must allocate a new, larger, memory block and copy/move the currently present elements in the vector into that new allocation. After that, the objects in the previous allocation are destroyed, involving the destructor call.
You are not seeing the copies being made, because you didn't define a custom copy constructor. If you did, you would see that a copy of A
is copy constructed before the original A
is destructed. If you define a noexcept
move constructor, you will see that a copy of A
is move constructed before the original A
is destructed.
Upvotes: 3