emplace_back() issue under VS2013

emplace_back() issue under VS2013

本文关键字:issue under VS2013 back emplace      更新时间:2023-10-16

考虑以下代码

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;
}

VS2013 输出

21
22
23
24
-17891602
22

为什么-17891602在这里

GCC 4.8.4的输出正确,如下所示

21
22
23
24
21
22

然后比较了VS2013GCCemplace_back的实现

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)...);
  }

VS2013中似乎使用了奇怪的_Reserve(1);。为什么?

编辑:

-17891602hex值为0xFEEEFEEE,表示

由Microsoft的调试HeapFree()用于标记已释放的堆内存

参考幻数

然后我逐行调试了上面的代码,发现_Reserve(1);引起的0XFEEEFEEE被调用了。

在VS2013和VS2015中将元素放置到包含该元素的向量中时,这是一个问题。如果向量大小调整,则对插入的元素的引用无效。解决方法是在insert中创建元素的副本,然后插入该副本。

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

_Reserve调用用于确保为向量分配了一些内存(因此在以后的操作中不必检查它)。

模板问题

绑定到emplace成员函数的函数参数包的对象应而不是容器元素的元素或子对象。

emplace_back()是在VS2013下的emplace()函数中调用的

  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);
    } 

我发现了一篇很好的帖子,它描述了VS2013emplace_back()实现的一些细节。

std::vector类具有不同的实例成员(常规和内部),其中包括以下成员:

  • _Myfirst-指向数据数组的开头
  • _Mylast-指向数据数组中第一个未初始化的元素。如果等于_Myend,则下一次插入将导致重新分配。你接到这个家伙的end()电话
  • _Myend-指向数据数组的末尾

因此,就存储器地址而言,发生了以下不等式:

_Myfirst <=<= _Mylast <=<= _Myend

看到那行_Reserve(1)了吗?这个函数调用导致我们的bug暴露出来。

让我们逐步完成(请参阅前面的示例函数)。

nums.emplace_back(nums[0]);

首先,由于operator[]返回reference ,因此我们获得了对该项目的引用

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

然后我们进入emplace_back方法,将新鲜有效的引用传递给要插入的项。我们在开始时立即看到的是对向量大小超出的检查。只要我们的插入导致向量的大小增加,我们就会在重新分配发生后使引用无效。这就是产生如此有趣但又令人期待(一旦我们开始实现)的行为的原因。