12.05.06

Illegal uses of Singular Iterator Values

Posted in cplusplus at 5:44 pm by Fernando Cacciola

Take a look at the following C++ code snippet:

Container::iterator i ;
Container::iterator j ;
i == j ;

Looks OK doesn’t it?

Well, to many people it does. Let’s see.

What’s the value of the variables i and j?

Formally speaking, a singular value. Practically speaking, it can be anything, because C++ doesn’t force any initialization (like zero, value or default initialization) of uninitialized variables. And yes, these are uninitialized variables for all you know.

A variable of a class type defined without any initializer list is default initialized, and you would be right expecting such a variable to have a non-singular value. At the same time, an iterator can be of a class type, and if it has a default constructor you could be right expecting it to intruduce a non-singular iterator variable. But iterators are not required to do that, so an arbitrary uninitialized iterator variable is not default initialized.

More often than not, people treat uninitialized iterator variables as if they were default initialized iterator variables, ignoring the fact that iterators are not required to be class types. Thus, it is unfortunately more or less common to find errouneous comparisons like the one shown at the beginning.

Why am I bringing this up? Because the STL that ships with Visual C++ 8 has checked iterators by default (in a debug build), and illegal uses of singular iterator values is one of the mistakes it checks. Even though these iterators are of class type, trying to compare two uninitialized iterator variables will assert. And rightly so.

Not incidentally I happen to be figuring out how to fix some illegal uses of uninitialized iterators in a code base. The problem only showed up now thanks to the VC8 STL.

The code base uses lots of custom made iterators, most of them wrapping standard iterators. So at first I thought I could simplify the change by simply disabling all default constructors in all wrapping iterators and change the code on a case by case basis. I figured: why would one need to define an uninitialized iterator variable anyway?, surely there’s always a better construct that avoid the uninitialized variable.

I figured wrong. There is one common idiom-used quite too often to disregard it-that needs uninitialized variables:

SomeIterator b,e ;
for ( boost::tie(b,e) = GetBothBeginAndEndAtOnce(Container); b != e ; ++ b )
...

So the only thing I can do is run the entire project testsuite and hunt each illegal uses (mostly comparisons) one by one.

Fortunately, there is a trick that allows me to run the testsuite unattended:

This line disables (in VC) the pop up windows that by deafult is shown each time an assert fails:

_set_error_mode(_OUT_TO_STDERR);

Or if you are linking to a debug CRT, these lines instead (the above works only in release CRTs):


_CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE );
_CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR );

Causing the assert message to go to stderr instead, which the test driver nicely captures in a log file.

Leave a Comment