簡體   English   中英

C ++ for-each語句觸發“vector iterators incompatible”斷言失敗:this - > _ Getcont()== 0

[英]C++ for-each statement triggers “vector iterators incompatible” assertion failure: this->_Getcont() == 0

這是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是NULL在_Iterator_base12 )。 調用堆棧是:

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 Vector_const_iterator什么?

載體來自容器

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

我終於找到了問題。 向量的設置代碼深處的路徑破壞了向量的狀態(memset將其設置為0,作為更大塊內存的一部分)。 這對vector中的前三個字段沒有任何影響: _Myfirst_Mylast_Myend因為這些字段在初始向量中為0。 此外,大多數事情,如數組索引運算符和其他方法,如push_back仍然正常運行。 但是,第四個字段_Myproxy最初是非零的,並且清除它禁用了基於迭代器的功能。 因此,for-each循環, 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循環是一個較舊的替代方法,可以避免使用迭代器,但繼續使用其他可用於范圍檢查動態容器(如vector)的方法,並保留在容器范圍內,以容納當前位於其中的元素數量。 該循環必須始終有效,因為循環體正在為容器內的每個元素(也是一個字符串,但不是一個常量字符串)分配一個本地分配的const字符串引用。 永遠不會嘗試將迭代器類型分配給第二個for循環體內的字符串引用。

這個新的范圍有很多很好的功能,以盡可能減少鍵盤輸入。 但是,最好使用auto關鍵字和后續數據類型分配,編譯器將始終正確(假設它永遠保持在C ++ 11合規性)。

斷言的失敗

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

對於空向量(開始編輯)也是完全正確的。

back()方法是我最近為STL添加的。 對不起。 在我出去之前急於完成原始帖子時,我讀了單詞back()並將其翻譯成我的大腦結束()。

如果調用end()方法:T&t與std :: vector<T> :: iterator ,這是調用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的原型,在沒有定義當前最后一個元素之后調用它,沒有參數插入。 參考頁面說:“如果使用適當的參數不支持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源代碼中發生的任何修改。

我自己遇到了這個斷言失敗。 我發現原因是我在循環中調用的東西正在修改我迭代的向量。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM