c++vectorstdemplace

What is happening in std::vector::emplace() implementation?


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.


Solution

  • 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.