I'm investigating how the emplace_back()
function actually works under the hood. Here's the code I'm using for my experiment:
#include <iostream>
#include <string>
#include <optional>
#include <utility>
#include <memory>
#include <array>
#include <vector>
using namespace std;
class base {
public:
base() = default;
base(int i) : i_{i} {cout << "Constructed at: " << this << " with i_ = " << I() << endl;}
base(base const &rhs) : i_ (rhs.i_) {cout << "Copy to : " << this << " with i_ = " << this->I() << " from " << &rhs << endl;}
base(base &&rhs) noexcept : i_ (move(rhs.i_)) {cout << "Move to : " << this << " with i_ = " << this->I() << " from " << &rhs << endl;}
virtual ~base() {cout << "Destructed at : " << this << " with i_ = " << I() << endl;}
virtual void whoAmI() const {cout << "I am base" << endl;}
virtual int I() const {return i_;}
private:
int i_ {0};
};
int main () {
vector<base> b_objs_em {};
cout << "\n--- Start of emplace_back() loop ---\n" << endl;
for (auto i {0}; i < 5; ++i) {
b_objs_em.emplace_back(base {i});
cout << "" << endl;
}
cout << "\n--- End of emplace_back() loop ---\n" << endl;
return 0;
}
Here's is the output that I get from the compiled code:
--- Start of emplace_back() loop ---
Constructed at: 0x7fff904635e0 with i_ = 0
Move to : 0x20fcec0 with i_ = 0 from 0x7fff904635e0
Destructed at : 0x7fff904635e0 with i_ = 0
Constructed at: 0x7fff904635e0 with i_ = 1
Move to : 0x20fcef0 with i_ = 1 from 0x7fff904635e0
Move to : 0x20fcee0 with i_ = 0 from 0x20fcec0
Destructed at : 0x20fcec0 with i_ = 0
Destructed at : 0x7fff904635e0 with i_ = 1
Constructed at: 0x7fff904635e0 with i_ = 2
Move to : 0x20fcf30 with i_ = 2 from 0x7fff904635e0
Move to : 0x20fcf10 with i_ = 0 from 0x20fcee0
Destructed at : 0x20fcee0 with i_ = 0
Move to : 0x20fcf20 with i_ = 1 from 0x20fcef0
Destructed at : 0x20fcef0 with i_ = 1
Destructed at : 0x7fff904635e0 with i_ = 2
Constructed at: 0x7fff904635e0 with i_ = 3
Move to : 0x20fcf40 with i_ = 3 from 0x7fff904635e0
Destructed at : 0x7fff904635e0 with i_ = 3
Constructed at: 0x7fff904635e0 with i_ = 4
Move to : 0x20fcfa0 with i_ = 4 from 0x7fff904635e0
Move to : 0x20fcf60 with i_ = 0 from 0x20fcf10
Destructed at : 0x20fcf10 with i_ = 0
Move to : 0x20fcf70 with i_ = 1 from 0x20fcf20
Destructed at : 0x20fcf20 with i_ = 1
Move to : 0x20fcf80 with i_ = 2 from 0x20fcf30
Destructed at : 0x20fcf30 with i_ = 2
Move to : 0x20fcf90 with i_ = 3 from 0x20fcf40
Destructed at : 0x20fcf40 with i_ = 3
Destructed at : 0x7fff904635e0 with i_ = 4
--- End of emplace_back() loop ---
Destructed at : 0x20fcf60 with i_ = 0
Destructed at : 0x20fcf70 with i_ = 1
Destructed at : 0x20fcf80 with i_ = 2
Destructed at : 0x20fcf90 with i_ = 3
Destructed at : 0x20fcfa0 with i_ = 4
For i = 0,1,2 and 4 - the behaviour is expected i.e.
(1) construct object on stack,
(2) move it to heap,
(3) move the contents of previously allocated objects to new locations on heap (which is contiguous with the new object) and deallocate memory from the old objects.
(4) destruct the original object on stack (created in (1))
My question is: why is the behaviour different for i = 3? Step (3) seems to be missing here.