C ++ for-each语句触发“向量迭代器不兼容”断言失败:this-> _Getcont()== 0

时间:2014-02-21 02:04:09

标签: c++ c++11 vector stl

这是Visual Studio 2012.

static void func(
  ...,
  const std::vector<std::string> &opt_extra_args_strs,
  ...)
{
   // THIS ASSERTS: "vector iterators incompatible"
   for (const std::string &arg_str : opt_extra_args_strs) {
      ... body does not modify opt_extra_args_strs

   // BUT THIS WORKS:
   for (size_t a_ix = 0; a_ix < opt_extra_args_strs.size(); a_ix++) {
       const std::string &arg_str = opt_extra_args_strs[a_ix];
}

我根本没有在循环体中修改向量,事实上,断言发生在第一次迭代之前。向量在调试器中看起来正确,但我不太了解STL以查找损坏。在STL中,断言失败来自:

void _Compat(const _Myiter& _Right) const {
    // test for compatible iterator pair
    if (this->_Getcont() == 0 // THIS FAILS (_Getcont() == 0)
         ...) {
        _DEBUG_ERROR("vector iterators incompatible");

this->_Getcont()为NULL,因为_Myproxy中的_Iterator_base12为NULL。 调用堆栈是:

msvcp110d.dll!std::_Debug_message(const wchar_t * message, const wchar_t * file, unsigned int line) Line 15 C++
Main.exe!std::_Vector_const_iterator<std::_Vector_val<std::_Simple_types<std::basic_string<char,std::char_traits<char>,std::allocator<char> > > > >::_Compat(const std::_Vector_const_iterator<std::_Vector_val<std::_Simple_types<std::basic_string<char,std::char_traits<char>,std::allocator<char> > > > > & _Right)
Main.exe!std::_Vector_const_iterator<std::_Vector_val<std::_Simple_types<std::basic_string<char,std::char_traits<char>,std::allocator<char> > > > >::operator==(const std::_Vector_const_iterator<std::_Vector_val<std::_Simple_types<std::basic_string<char,std::char_traits<char>,std::allocator<char> > > > > & _Right)
Main.exe!std::_Vector_const_iterator<std::_Vector_val<std::_Simple_types<std::basic_string<char,std::char_traits<char>,std::allocator<char> > > > >::operator!=(const std::_Vector_const_iterator<std::_Vector_val<std::_Simple_types<std::basic_string<char,std::char_traits<char>,std::allocator<char> > > > > & _Right)
Main.exe!run_test(..., const std::vector<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::allocator<std::basic_string<char,std::char_traits<char>,std::allocator<char> > > > & opt_extra_args_strs)
    ...

我怀疑设置矢量的代码是以某种方式搞砸了,但我不确定。我也很难编写一个更简单的复制器,但程序应该是完全确定的(单线程,而不是随机变量,总是断言)。

此外,我还遇到了一个不同的类似断言失败"vector iterator + offset out of range"与片段(在同一个向量上)

template <typename T>
class Elsewhere {
    virtual void earlier(
        ....
        std::vector<T> &v) const
    {
       v.emplace_back(); // empty construction of a T
      // T &t = v.back(); // assertion failure
      T &val = to[to.size() - 1]; // but this works
      ... mutates val.

使用T = std::string(实际上是相同的向量)。

我提到这一点是因为在STL中,这种失败的条件最终也是this->_Getcont() == 0,我怀疑它们是相关的。 _Getcont()Vector_const_iterator为0是什么意思?

矢量来自容器

template <typename T>
struct type {
   T m_value;

   operator const T &() const {
     return value();
   }

   const T &value() const {
       return m_value;
   }
};

type<std::vector<std::string>> &t = ... method call that returns ref to it;
... t gets set
func(t); // implicit conversion to (const std::vector<std::string> &)

3 个答案:

答案 0 :(得分:3)

我终于找到了问题。向量的设置代码深处的路径破坏了向量的状态(memset将其设置为0,作为更大块内存的一部分)。这对向量中的前三个字段没有任何影响:_Myfirst_Mylast_Myend,因为这些字段在初始向量中为0。此外,大多数事情,如数组索引运算符和其他方法,如push_back仍然正常运行。但是,第四个字段_Myproxy最初为非零,并清除它禁用基于迭代器的功能。因此,for-each循环,vector::back()和其他循环都会因不同的错误错误而失败,例如错误的边界检查,错误的不兼容的迭代器等等。

答案 1 :(得分:1)

关于具有范围的循环的第一个项目,除非C ++ 11标准中有新内容,否则不兼容的迭代器消息是100%准确的。该行试图从向量中为迭代器分配一个常量字符串引用。迭代器类型与要存储的元素的数据类型不兼容。

请查看http://www.stroustrup.com/C++11FAQ.html#for。在该示例中,使用auto数据类型,以便通过冒号使用的迭代器的底层设置设置在容器的开头(例如键盘输入比旧时少得多)。然后更多的事情发生,确保迭代器永远不会传递最后一个元素。上面循环的范围(如所写)相当于:

// added a local string to clearly indicate types
std::string s1;
const std::string &arg_str = s1;


const std::vector<std::string> :: iterator i;
i = opt_extra_args_strs.begin(); // happens inside the range for

for (arg_str = i; i < opt_extra_args_strs.end(); i++)
{
   // loop body
}

只要将arg_str引用重新分配给迭代器的起始点,编译器就会抱怨并打印错误。

第二个for循环是一个较旧的替代方法,可以避免使用迭代器,但继续使用其他可用于范围检查动态容器(如vector)的方法,并保留在当前其中元素数量的容器范围内。该循环必须始终有效,因为循环体正在为容器内的每个元素(也是一个字符串,但不是一个常量字符串)分配一个本地分配的const字符串引用。永远不会尝试将迭代器类型分配给第二个for循环体内的字符串引用。

的新范围有许多不错的功能,可以尽可能减少键盘输入。但是,最好使用auto关键字和后续数据类型赋值,编译器将始终正确(假设它永远保持在C ++ 11中)。

的断言失败
T &t = v.back(); // assertion failure
对于空矢量(开始编辑)

也完全正确。

对我来说,back()方法是STL的最新成员。对不起。在我出去之前急于完成原始帖子时,我读了单词back()并将其翻译成我的大脑结束()。

如果调用end()方法:T&amp; t与std :: vector<T> :: iterator的类型不同,这是调用v.end()时返回的内容。在类型方面,它看起来更像是:

 T &t = std::vector<T> :: iterator

在查看std :: vector :: back()的详细信息后,如果在空向量上调用back(),则行为 undefined 。更有可能的是,只有在运行时才能找到漏洞。如需参考,请尝试:http://www.cpluscplus.com/reference/vector/vector/back。对于总是调用back(),必须确认的第一件事是向量中至少存在一个元素。在此之前,由于emplace_back(Args&&... args);http://www.cpluscplus.com/reference/vector/vector/emplace_back的原型,因此在没有定义当前最后一个元素之后插入没有参数的调用它。参考页面说:“如果适当的参数不支持allocator_traits :: construct,则会导致未定义的行为。”代码可能必须变得更像:

//
// start of the 'earlier' function body 
//
std::string s;
v.emplace_back(s); // add one element to v

//
// obtain a reference to the last element (should be a copy of s above)
//
T &t = v.back(); 



//
// It is not clear what the vector 'to' is and how it exists inside 'earlier'
// as long as 'to' has at least one element, then the code below will 
// set the local reference variable to the last element of 'to'.  
// If not, then another run time error is likely with attempting to access to[-1]
// and then attempting to assign the non-existent element to T& val
//
T &val = to[to.size() - 1];

我希望这有助于理解迭代器,在当前最后一个元素之后添加元素以及存储在容器中的数据元素类型之间的区别。

(结束编辑)

内存分配区域是一个问题,这将是非常令人惊讶的。 Visual Studio 2012会有许多不满意的C ++工程师。强烈建议撤消stl源代码中发生的任何修改。

答案 2 :(得分:1)

我自己遇到了这个断言失败。我发现原因是我在循环中调用的东西正在修改我正在迭代的向量。