1

I implement my own vector class just to practice my C++ skill and my emplace() method is way slower than std::vector::emplace() so I checked the source code to see how they implemented it but it is just pain for me since that. I can't wrap my head around this implementation. I also found the implementation a bit hard to read but it might just be me.

This is what I got from source code.

template<typename _Tp, typename _Alloc>
     template<typename... _Args>
       typename vector<_Tp, _Alloc>::iterator
       vector<_Tp, _Alloc>::
       emplace(iterator __position, _Args&&... __args)
       {
     const size_type __n = __position - begin();
     if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage
         && __position == end())
       {
         this->_M_impl.construct(this->_M_impl._M_finish,
                     std::forward<_Args>(__args)...);
         ++this->_M_impl._M_finish;
       }
     else
       _M_insert_aux(__position, std::forward<_Args>(__args)...);
     return iterator(this->_M_impl._M_start + __n);
       }

What exactly is going on in this piece of code?

My beginner c++ intuition was telling me that when size == capacity, then creates new dynamic array, move all objects to new created array, change value at iterated address and remember the removed object, replace the next object with the removed object and goes like that.

Yksisarvinen
  • 18,008
  • 2
  • 24
  • 52
  • of course you should follow `_M_impl` and see what it does as IMPLementation. – apple apple Mar 01 '23 at 15:43
  • You need to go further, look at the `_M_impl` member implementation and to what its `construct()` member function does (you'll probably find some sort of placement-new there at some point). The arguments are passed using perfect forwarding as you can see here. – Fareanor Mar 01 '23 at 15:43
  • 1
    It says: when inserting a new element at the end and there is room for it, then construct it there. Else, call _M_insert_aux, which is probably a more complicated insert function that handles all the other cases. Yes, for some reason standard library code is always hard to read. Don't know why. Ask the people who wrote it. – user253751 Mar 01 '23 at 15:44
  • @appleapple That's where I got stuck actually, I couldn't follow _M_impl and _M_insert_aux methods – fettahyildiz Mar 01 '23 at 20:38

1 Answers1

3

Your "beginner C++ intuition" is correct except that most of that is not actually happening right here.

     if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage
         && __position == end())

This is the emplace() overload that takes an iterator to where the new object gets emplaced. This checks if the emplacement position is at the end of the vector and the vector has not reached its capacity, if so the new value can be constructed in place, in the vector's derrière, otherwise:

 _M_insert_aux(__position, std::forward<_Args>(__args)...);

this calls another function where all of the work you described takes place, which involves things like shifting all existing values in the vector on or after the insertion position, by one, and/or enlarging the vector. So, all of the fun stuff you described happens over there, and not in here.

This is just the easy case, where the emplace takes care of business at the end of the vector, and the vector has room for growth. Otherwise, go over there, and see where the real work takes place.

The only other thing that's happening here is the icing on the cake: after all is said and done this function figures out the iterator to the newly-emplaced value, in the vector, and returns it, as it's required to do.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148