VS2013下的emplace_back()问题

时间:2016-01-04 06:09:37

标签: c++ c++11 gcc visual-studio-2013

考虑以下代码

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

然后我比较emplace_backVS2013之间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)...);
  }

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

编辑:

hex的{​​{1}}值为-17891602,表示

  

Microsoft使用调试HeapFree()来标记释放的堆内存

参考magic number

然后我逐行调试了上述代码,发现0xFEEEFEEE引起的0XFEEEFEEE被调用。

2 个答案:

答案 0 :(得分:12)

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

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

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

答案 1 :(得分:2)

The emplace issue

  

绑定到function loadi(srce){ return loadImage(srce); } function loadImage(src) { return new Promise(function(resolve, reject) { $img1.attr('src',src).on('load',function(error){ resolve(); }); // Run image loading logic here // Call resolve() when loading complete, or reject() when fails. }); } loadImage('1.png') .then(loadi('2.png')) .then(loadi('3.png')) .then(function() { console.log('Load successful!'); }) // Not in loadImage(). .catch(function(err) { console.log("Error");/* Handle potential errors */ }); 成员函数的函数参数包的对象是容器元素的元素或子对象。

emplace下的emplace_back()函数中调用emplace()

<击>

<击>
VS2013

<击>

我找到了一个好的post,其中描述了 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); } emplace_back()实施的一些细节。

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

  • std::vector - 指向数据数组的开头
  • _Myfirst - 指向数据数组中的第一个未初始化元素。如果等于_Myend,则下一次插入将导致重新分配。你可以通过_Mylast来电
  • 找到这个人
  • end() - 指向数据数组的末尾

因此,就内存地址而言,会发生以下不等式:

_Myend

查看其中包含_Myfirst <=<= _Mylast <=<= _Myend的行?这个函数调用导致我们的bug显露出来。

让我们一步一步地工作(参考前面的示例函数)。

_Reserve(1)

首先我们得到一个引用,因为nums.emplace_back(nums[0]); 会返回operator[]

reference

然后我们进入reference operator[](size_type _Pos) { ... } 方法,将新的有效引用传递给我们要插入的项目。我们在开始时立即看到的是检查向量的大小超过。只要我们的插入导致向量增加其大小,我们就会在重新分配发生后立即使引用失效。这就是这种有趣但是预期(一旦我们开始实施)行为的原因。