C++ for-each 语句触发"vector iterators incompatible"断言失败:this->_Getcont() == 0

C++ for-each statement triggers "vector iterators incompatible" assertion failure: this->_Getcont() == 0

本文关键字:gt Getcont this- 失败 语句 for-each vector iterators C++ 断言 incompatible      更新时间:2023-10-16

这是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");

其中CCD_ 1为NULL,因为(CCD_。调用堆栈为:

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> &)

我终于发现了问题。向量设置代码中的一条路径破坏了向量的状态(将其记忆为0,作为更大内存块的一部分)。这对向量中的前三个字段_Myfirstthis->_Getcont()0和_Myend没有造成危害,因为这些字段在初始向量中为0。此外,大多数东西(如数组索引运算符)和其他方法(如push_back)仍然正常工作。然而,第四个字段_Myproxy最初是非零的,清除它将禁用基于迭代器的功能。因此,对于每个循环,vector::back()和其他循环都会因不同的错误而失败,例如错误边界检查、错误不兼容迭代器等

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

关于循环的范围的第一项,除非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循环是一个旧的替代方法,可以避免迭代器,但继续使用其他方法来检查动态容器(如向量)的范围,并保持在当前容器内元素数量的容器边界内。该循环必须始终有效,因为循环主体将本地分配的const字符串引用分配给容器中的每个元素(也是一个字符串,但不是常量字符串)。迭代器类型从未尝试分配给第二个for循环体中的字符串引用。

的这个新系列有很多不错的功能,可以尽可能减少键盘打字。然而,最好使用auto关键字和编译器始终正确的后续数据类型赋值(假设它永远符合C++11)。

断言失败

T &t = v.back(); // assertion failure

对于空矢量也是完全正确的(开始编辑)

back()方法是我最近在STL中添加的。很抱歉。在出发前匆忙完成最初的帖子时,我读了单词back(),并在脑子里把它翻译成了end()。

如果调用end()方法:;t与std :: vector<T> :: iterator的类型不同,CCD_14是调用v.end()时返回的类型。就类型而言,它看起来更像这样:

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

在查看了std::vector::back()的详细信息后,如果在空向量上调用back(),则行为为未定义。更有可能的是,只有在运行时才能发现缺陷。请尝试以下操作以供参考:http://www.cpluscplus.com/reference/vector/vector/back.对于总是调用back(),首先需要确认的是向量中至少存在一个元素。在此之前,由于emplace_back(Args&&... args);是http://www.cpluscplus.com/reference/vector/vector/emplace_back,在没有定义当前最后一个元素之后调用它,而不使用要插入的参数。参考页面上写着,"如果allocater_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源代码中发生的任何修改。