1

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.

wohlstad
  • 12,661
  • 10
  • 26
  • 39
Ahmed R
  • 13
  • 3
  • 1
    (You forgot to trace the assignment operator. See Rule of Three (Five).) But the reason is that there was no need to move the elements when adding number 3. There was enough room in the vector to store 3 without having to move items 0, 1, or 2. If every previous element moved on every `emplace_back`, then it would not be able to achieve the required amortized constant complexity. (Note also that you are missing the point of `emplace_back`. The point is to let you write `emplace_back(i)` to avoid the extra move.) [Background reading](https://stackoverflow.com/q/46958273). – Raymond Chen Aug 14 '22 at 14:43
  • Some relevant info about vector size vs capacity: https://stackoverflow.com/questions/6296945/size-vs-capacity-of-a-vector. – wohlstad Aug 14 '22 at 16:05
  • I think this post answers your question: https://stackoverflow.com/questions/11560904/vector-memory-allocation-strategy. – Theo Aug 16 '22 at 13:06

0 Answers0