16

Consider the following codes

std::vector<int> nums{21, 22, 23, 24};
nums.emplace_back(nums[0]);
nums.emplace_back(nums[1]);

for (auto n : nums) {
    std::cout << n << std::endl;
}

Output of VS2013

21
22
23
24
-17891602
22

Why the -17891602 is here?

Output of GCC 4.8.4is correct as following

21
22
23
24
21
22

Then I compare the implementation of emplace_back between VS2013 and GCC

VS2013

template<class... _Valty>
    void emplace_back(_Valty&&... _Val)
    {   // insert by moving into element at end
    if (this->_Mylast == this->_Myend)
        _Reserve(1);
    _Orphan_range(this->_Mylast, this->_Mylast);
    this->_Getal().construct(this->_Mylast,
        _STD forward<_Valty>(_Val)...);
    ++this->_Mylast;
    }

GCC

template<typename _Tp, typename _Alloc>
template<typename... _Args>
  void
  vector<_Tp, _Alloc>::
  emplace_back(_Args&&... __args)
  {
    if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage)
      {
        _Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,
                                 std::forward<_Args>(__args)...);
        ++this->_M_impl._M_finish;
      }
    else
      _M_emplace_back_aux(std::forward<_Args>(__args)...);
  }

It seems the weird _Reserve(1); is used in VS2013. Why?

Edit:

The hex value of -17891602 is 0xFEEEFEEE, which means

Used by Microsoft's debug HeapFree() to mark freed heap memory

refer to magic number

Then I debugged the above codes line by line and found the 0XFEEEFEEE caused by _Reserve(1); invoked.

zangw
  • 43,869
  • 19
  • 177
  • 214
  • Possible duplicate: [Can std::vector emplace_back copy construct from an element of the vector itself?](http://stackoverflow.com/questions/24908718/can-stdvector-emplace-back-copy-construct-from-an-element-of-the-vector-itself). – cpplearner Jan 04 '16 at 10:22
  • In this question, I want to know why `_Reserve(1)` is used in `VS2013`? different with the link above. – zangw Jan 04 '16 at 12:27

2 Answers2

12

This is a problem in VS2013 and VS2015 when emplacing an element into a vector that contains the element. If the vector resizes, the reference to the element being inserted is invalid. The work around is to create a copy of the element in insert, then insert that.

auto n = nums[0];
nums.emplace_back(n);

The _Reserve call is there to ensure there is some memory allocated for the vector (so it doesn't have to be checked for in later operations).

1201ProgramAlarm
  • 32,384
  • 7
  • 42
  • 56
  • 3
    It's a workaround for an unfortunate design choice. A similar workaround is `.emplace_back(nums[0]+0)`. Adding 0 shouldn't change the outcome, logically! – MSalters Jan 04 '16 at 09:21
  • 2
    @hvd See the discussion in http://open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#2164. The current wording seems to require this to work. – T.C. Jan 04 '16 at 13:22
  • 1
    @T.C. Thanks for the reference. It's `nums.emplace_back(std::move(n));` that's not required to work; I missed that since `emplace_back` takes a forwarding reference which becomes an lvalue reference here, the rule that makes this invalid doesn't apply, even though that's specifically mentioned in a note in [res.on.argument]1.3. I've deleted my previous comment. Apparently for a long time, the plan was to formally make it invalid for lvalue references too, according to the link to #760 in there. –  Jan 04 '16 at 13:47
  • LWG 2164 is about `emplace`, not `emplace_back`. The true difference between these implementations is in the code not shown: what happens in the reallocation case. If the new object construction throws an exception, there must not be any effects. The only way I know to do this is to construct the new object in the new buffer first, and then copy or move the old elements from the old buffer to the new buffer. If move constructions are not noexcept, copy must be used. Either VS2013 has figured out another way to do this, or they are always using copy to move the old elements to the new buffer. – Howard Hinnant Jan 05 '16 at 03:54
  • Or perhaps they are not giving the strong exception safety guarantee. – Howard Hinnant Jan 05 '16 at 04:00
2

The emplace issue

Objects bound to the function parameter pack of the emplace member function shall not be elements or sub-objects of elements of the container.

The emplace_back() is called in the emplace() function under VS2013.

  template<class... _Valty>
    iterator emplace(const_iterator _Where, _Valty&&... _Val)
    {   // insert by moving _Val at _Where
    size_type _Off = _VIPTR(_Where) - this->_Myfirst;

 #if _ITERATOR_DEBUG_LEVEL == 2
    if (size() < _Off)
        _DEBUG_ERROR("vector emplace iterator outside range");
 #endif /* _ITERATOR_DEBUG_LEVEL == 2 */

    emplace_back(_STD forward<_Valty>(_Val)...);
    _STD rotate(begin() + _Off, end() - 1, end());
    return (begin() + _Off);
    } 

I found one good post, which describe some details of emplace_back() implementation under VS2013.

std::vector class has different instance members (both regular and internal) and among them are the following:

  • _Myfirst - points to the beginning of the data array
  • _Mylast - points to the first uninitialized element in the data array. If equals to _Myend, next insertion will cause reallocation. You get this guy on end() call
  • _Myend - points to the end of the data array

So, in terms of memory addresses, the following inequality takes place:

_Myfirst <=<= _Mylast <=<= _Myend

See that line with _Reserve(1) in it? This function call causes our bug to reveal itself.

Let's work through step-by-step (refer to previous example function).

nums.emplace_back(nums[0]);

First we get a reference to the item because operator[] returns a reference

reference operator[](size_type _Pos) 
{ ... }

Then we move into emplace_back method, passing fresh and valid reference to the item we want to insert. What we immediately see at the beginning is a check on vector's size exceeding. As long as our insertion causes a vector to grow its size, we get reference invalidated just after reallocation happens. That's the reason of such interesting but expected (once we got into implementation) behavior.

zangw
  • 43,869
  • 19
  • 177
  • 214
  • 1
    That's a proposed resolution that LWG apparently disagrees with. Besides, that doesn't affect `emplace_back`. – T.C. Jan 04 '16 at 13:23