Debug Iterator Support
The Visual C++ run-time library detects incorrect iterator use, and asserts and displays a dialog box at run time. To enable debug iterator support, you must use debug versions of the C++ Standard Library and C Runtime Library to compile your program. For more information, see CRT Library Features. For information about how to use checked iterators, see Checked Iterators.
The C++ standard describes how member functions might cause iterators to a container to become invalid. Two examples are:
Erasing an element from a container causes iterators to the element to become invalid.
Increasing the size of a vector by using push or insert causes iterators into the
vector
to become invalid.
If you compile this sample program in debug mode, at run time it asserts and terminates.
// iterator_debugging_0.cpp
// compile by using /EHsc /MDd
#include <vector>
#include <iostream>
int main() {
std::vector<int> v {10, 15, 20};
std::vector<int>::iterator i = v.begin();
++i;
std::vector<int>::iterator j = v.end();
--j;
std::cout << *j << '\n';
v.insert(i,25);
std::cout << *j << '\n'; // Using an old iterator after an insert
}
You can use the preprocessor macro _ITERATOR_DEBUG_LEVEL to turn off the iterator debugging feature in a debug build. This program doesn't assert, but still triggers undefined behavior.
// iterator_debugging_1.cpp
// compile by using: /EHsc /MDd
#define _ITERATOR_DEBUG_LEVEL 0
#include <vector>
#include <iostream>
int main() {
std::vector<int> v {10, 15, 20};
std::vector<int>::iterator i = v.begin();
++i;
std::vector<int>::iterator j = v.end();
--j;
std::cout << *j << '\n';
v.insert(i,25);
std::cout << *j << '\n'; // Using an old iterator after an insert
}
20
-572662307
An assert also occurs if you attempt to use an iterator before it's initialized, as shown here:
// iterator_debugging_2.cpp
// compile by using: /EHsc /MDd
#include <string>
using namespace std;
int main() {
string::iterator i1, i2;
if (i1 == i2)
;
}
The following code example causes an assertion because the two iterators to the for_each algorithm are incompatible. Algorithms check to determine whether the iterators that are supplied to them reference the same container.
// iterator_debugging_3.cpp
// compile by using /EHsc /MDd
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
vector<int> v1 {10, 20};
vector<int> v2 {10, 20};
// The next line asserts because v1 and v2 are
// incompatible.
for_each(v1.begin(), v2.end(), [] (int& elem) { elem *= 2; } );
}
Notice that this example uses the lambda expression [] (int& elem) { elem *= 2; }
instead of a functor. Although this choice has no bearing on the assert failure—a similar functor would cause the same failure—lambdas are a way to write a short block of code. For more information about lambda expressions, see Lambda expressions.
Debug iterator checks also cause an iterator variable that's declared in a for
loop to be out of scope when the for
loop scope ends.
// iterator_debugging_4.cpp
// compile by using: /EHsc /MDd
#include <vector>
#include <iostream>
int main() {
std::vector<int> v {10, 15, 20};
for (std::vector<int>::iterator i = v.begin(); i != v.end(); ++i)
; // do nothing
--i; // C2065
}
Debug iterators have non-trivial destructors. If a destructor doesn't run but the object's memory is freed, access violations and data corruption might occur. Consider this example:
// iterator_debugging_5.cpp
// compile by using: /EHsc /MDd
#include <vector>
struct base {
// TO FIX: uncomment the next line
// virtual ~base() {}
};
struct derived : base {
std::vector<int>::iterator m_iter;
derived( std::vector<int>::iterator iter ) : m_iter( iter ) {}
~derived() {}
};
int main() {
std::vector<int> vect( 10 );
base * pb = new derived( vect.begin() );
delete pb; // doesn't call ~derived()
// access violation
}