Does an iterator have a default value? [duplicate] - c++

For any STL container that I'm using, if I declare an iterator (of this particular container type) using the iterator's default constructor, what will the iterator be initialised to?
For example, I have:
std::list<void*> address_list;
std::list<void*>::iterator iter;
What will iter be initialised to?

By convention a "NULL iterator" for containers, which is used to indicate no result, compares equal to the result of container.end().
std::vector<X>::iterator iter = std::find(my_vec.begin(), my_vec.end(), x);
if (iter == my_vec.end()) {
//no result found; iter points to "nothing"
}
However, since a default-constructed container iterator is not associated with any particular container, there is no good value it could take. Therefore it is just an uninitialized variable and the only legal operation to do with it is to assign a valid iterator to it.
std::vector<X>::iterator iter; //no particular value
iter = some_vector.begin(); //iter is now usable
For other kinds of iterators this might not be true. E.g in case of istream_iterator, a default-constructed iterator represents (compares equal to) an istream_iterator which has reached the EOF of an input stream.

The default constructor initializes an iterator to a singular value:
Iterators can also have singular values that are not associated with any sequence.
[ Example: After the declaration of an uninitialized pointer x (as with int* x;),
x must always be assumed to have a singular value of a pointer. — end example ]
Results of most expressions are undefined for singular values
[24.2.1 §5]

The iterator is not initialized, just as int x; declares an integer which isn't initialized. It does not have a properly defined value.

Related

Can I initialize an iterator with null, and if not why?

I'm trying to initialize an iterator to NULL, but it's not working. Can anyone help me?
If a pointer can be initialized with null, why can't we do that for an iterator?
vector<int> bob;
vector<int>::iterator it=NULL;
I want to initialize an iterator in a class constructor so that at the time of creation of an object of the class, the iterator should be set to NULL (default).
No, in general you cannot initialize an iterator with NULL. The iterator requirements do not require an iterator to be assignable or initializable from either an integer type or std::nullptr_t, the possible types that NULL can have.
There is no point in trying to do that. It is simply not needed. But since you have not explained why you would try to do that, I can't really make any further suggestions.
Regarding your further questions in the comments: You can value-initialize every forward iterator:
vector<int>::iterator it{}; // value-initialized
Since C++14 you are guaranteed that comparing iterators of the same type constructed in this way compare equal.
All container iterators are forward iterators.
The best equivalent to NULL or null_ptr for an iterator is the container::end() value. It likewise does not point to a valid element of the container. So, instead of testing the predicate (i == null_ptr), test (i == v.end()), and initialize as auto i = v.end();.
You can't initialize the iterator until after you have a container. But that is not a problem in practice because an iterator makes no sense without a container it refers to.
If you have complicated code that uses iterators, and you want to isolate that code in a function, you will have to pass two iterators to the function: the current or beginning iterator, and the end iterator. This is what the STL does.
An iterator is initialized when created.
You can null check its _Ptr member:
vector<int>::iterator p;
if (p._Ptr == NULL) ...
(using clang & dinkumware)

Is it well-defined to compare with a value-initialized iterator?

Does the following program invoke undefined behavior?
#include <iostream>
#include <iterator>
int main(int argc, char* argv[])
{
for (auto it = std::istream_iterator<std::string>(std::cin);
it != std::istream_iterator<std::string>();
++it)
{
std::cout << *it << " ";
}
return 0;
}
This 4 year old question says that they can't be compared:
Iterators can also have singular values that are not associated with
any container. [Example: After the declaration of an uninitialized
pointer x (as with int* x;), x must always be assumed to have a
singular value of a pointer. ] Results of most expressions are
undefined for singular values; the only excep- tion is an assignment
of a non-singular value to an iterator that holds a singular value.
But another answer for says for the C++14 standard:
However, value-initialized iterators may be compared and shall compare
equal to other value-initialized iterators of the same type.
You are conflating two different issues.
istream_iterator is an input iterator, not a forward iterator, so the C++14 change you cited doesn't apply to it at all. You are allowed to compare istream_iterators in that manner because they are explicitly specified to allow such comparisons. The standard says that (§24.6.1 [istream.iterator])
The constructor with no arguments istream_iterator() always
constructs an end-of-stream input iterator object, which is the only
legitimate iterator to be used for the end condition. [...]
Two end-of-stream iterators are always equal. An end-of-stream
iterator is not equal to a non-end-of-stream iterator. Two
non-end-of-stream iterators are equal when they are constructed from
the same stream.
For forward iterators (which also includes bidirectional and random access ones) in general, value-initialized iterators are made comparable to each other in C++14. If your standard library implements it, then you can compare two value-initialized iterators. This allows you to create an empty range without an underlying container. However, you are still not allowed to compare a non-singular iterator to a value-initialized iterator. The following code has undefined behavior even in C++14:
std::list<int> l;
if(l.begin() == std::list<int>::iterator())
foo();
else
bar();

Is ->second defined for iterator my_map.end()?

I'm working with a std::map<std::string, MyClass* >.
I want to test if my_map.find(key) returned a specific pointer.
Right now I'm doing;
auto iter = my_map.find(key);
if ((iter != my_map.end()) && (iter->second == expected)) {
// Something wonderful has happened
}
However, the operator * of the iterator is required to return a reference. Intuitively I'm assuming it to be valid and fully initialized? If so, my_map.end()->second would be NULL, and (since NULL is never expected), I could reduce my if statement to:
if (iter->second == expected)
Is this valid according to specification? Does anyone have practical experience with the implementations of this? IMHO, the code becomes clearer, and possibly a tiny performance improvement could be achieved.
Intuitively I'm assuming it to be valid and fully initialized?
You cannot assume an iterator to an element past-the-end of a container to be dereferenceable. Per paragraph 24.2.1/5 of the C++11 Standard:
Just as a regular pointer to an array guarantees that there is a pointer value pointing past the last element
of the array, so for any iterator type there is an iterator value that points past the last element of a
corresponding sequence. These values are called past-the-end values. Values of an iterator i for which the
expression *i is defined are called dereferenceable. The library never assumes that past-the-end values are
dereferenceable. [...]
However, the operator *of the iterator is required to return a reference. Intuitively I'm assuming it to be valid and fully initialized?
Your assumption is wrong, dereferencing iterator that points outside of container will lead to UB.
24.2 Iterator requirements [iterator.requirements]
24.2.1 In general [iterator.requirements.general]
7 Most of the library’s algorithmic templates that operate on data structures have interfaces that use ranges.
A range is a pair of iterators that designate the beginning and end of the computation. A range [i,i) is an
empty range; in general, a range [i,j) refers to the elements in the data structure starting with the element
pointed to by i and up to but not including the element pointed to by j. Range [i,j) is valid if and only if
j is reachable from i. The result of the application of functions in the library to invalid ranges is undefined.
Even without checking the specs, you can easily see that dereferencing an iterator at end has to be invalid.
A perfectly natural implementation (the de-factor standard implementation for vector<>) is for end() to be literally a memory pointer that has a value of ptr_last_element + 1, that is, the pointer value that would point to the next element - if there was a next element.
You cannot possibly be allowed to dereference the end iterator because it could be a pointer that would end up pointing to either the next object in the heap, or perhaps an overflow guard area (so you would dereference random memory), or past the end of the heap, and possibly outside of the memory space of the process, in which case you might get an Access Violation exception when dereferencing).
If iter == my_map.end (), then dereferencing it is undefined behavior; but you're not doing that here.
auto iter = my_map.find(key);
if ((iter != my_map.end()) && (iter->second == expected)) {
// Something wonderful has happened
}
If iter != my_map.end() is false, then the second half of the expression (iter->second == expected) will not be exectuted.
Read up on "short-circut evaluation".
Analogous valid code for pointers:
if ( p != NULL && *p == 4 ) {}

What is an iterator's default value?

For any STL container that I'm using, if I declare an iterator (of this particular container type) using the iterator's default constructor, what will the iterator be initialised to?
For example, I have:
std::list<void*> address_list;
std::list<void*>::iterator iter;
What will iter be initialised to?
By convention a "NULL iterator" for containers, which is used to indicate no result, compares equal to the result of container.end().
std::vector<X>::iterator iter = std::find(my_vec.begin(), my_vec.end(), x);
if (iter == my_vec.end()) {
//no result found; iter points to "nothing"
}
However, since a default-constructed container iterator is not associated with any particular container, there is no good value it could take. Therefore it is just an uninitialized variable and the only legal operation to do with it is to assign a valid iterator to it.
std::vector<X>::iterator iter; //no particular value
iter = some_vector.begin(); //iter is now usable
For other kinds of iterators this might not be true. E.g in case of istream_iterator, a default-constructed iterator represents (compares equal to) an istream_iterator which has reached the EOF of an input stream.
The default constructor initializes an iterator to a singular value:
Iterators can also have singular values that are not associated with any sequence.
[ Example: After the declaration of an uninitialized pointer x (as with int* x;),
x must always be assumed to have a singular value of a pointer. — end example ]
Results of most expressions are undefined for singular values
[24.2.1 §5]
The iterator is not initialized, just as int x; declares an integer which isn't initialized. It does not have a properly defined value.

Comparing default-constructed iterators with operator==

Does the C++ Standard say I should be able to compare two default-constructed STL iterators for equality? Are default-constructed iterators equality-comparable?
I want the following, using std::list for example:
void foo(const std::list<int>::iterator iter) {
if (iter == std::list<int>::iterator()) {
// Something
}
}
std::list<int>::iterator i;
foo(i);
What I want here is something like a NULL value for iterators, but I'm not sure if it's legal. In the STL implementation included with Visual Studio 2008, they include assertions in std::list's operator==() that preclude this usage. (They check that each iterator is "owned" by the same container and default-constructed iterators have no container.) This would hint that it's not legal, or perhaps that they're being over-zealous.
OK, I'll take a stab. The C++ Standard, Section 24.1/5:
Iterators can also have singular
values that are not associated with
any container. [Example: After the
declaration of an uninitialized
pointer x (as with int* x;), x must
always be assumed to have a singular
value of a pointer. ] Results of most
expressions are undefined for singular
values; the only excep- tion is an
assignment of a non-singular value to
an iterator that holds a singular
value.
So, no, they can't be compared.
This is going to change in C++14. [forward.iterators] 24.2.5p2 of N3936 says
However, value-initialized iterators may be compared and shall compare
equal to other value-initialized iterators of the same type.
I believe you should pass a range to the function.
void fun(std::list<int>::iterator beg, std::list<int>::iterator end)
{
while(beg != end)
{
// do what you want here.
beg++;
}
}
Specification says that the postcondition of default constructor is that iterator is singular. The comparison for equality are undefined, so it may be different in some implementation.