What Is an Iterator's Default Value

What is an iterator's default value?

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.

Understanding iterator default value

Why does the iterator returns the default value twice?

The iterator returns the default value as many times at it is called, not only twice. The problem occurs because you defined x and w as follows:

x = iter(range(3))
y = iter(range(3))
w = ([next(y, "a"), next(y, "b")] for z in x)

Your w list has 3 entries (it iterates over the 3 elements in x), and when you call next(w) a fourth time, it throws an exception. Try setting x = iter(range(5)), and you will see that you can now call print(next(w)) 5 times. The inner next calls will return the default values as many times as called.

To confirm this, just output w after creating it, so you can see its contents:

x = iter(range(3))
y = iter(range(3))
w = ([next(y, "a"), next(y, "b")] for z in x)
list(w)

> [[0, 1], [2, 'b'], ['a', 'b']]

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

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.

Rust optional iterator default value

It depends whether x should be an Iterator or a Vec.

Iterator

let x = i.into_iter().flatten().chain(iter::repeat(None)).take(3);

Here's every step explained:

  1. into_iter() creates an iterator from our Option. Our next() call would return the whole Vec.
  2. flatten iterates the Vec and thus all its elements
  3. chain(iter::repeat(None)) adds unlimited Nones to the iteration
  4. take(3) limits the total amount of elements to three.

However, this assumes that you want to have exactly three elements always and either limit the Vec or fill it up with Nones.

Vec

Since take(3) returns an iterator, but unwrap_or returns a Vec on an existing vector, we need to transform the iterator into a Vec via collect():

let x = i.unwrap_or(iter::repeat(None).take(3).collect());

Now the types match.

Using iter on a list with a default value

The Pythonic way would usually be to use a for loop:

for val in foo:
print(val)

or if that doesn't work, put the default value in the next call instead of attaching it to the iterator itself:

bar = iter(foo)
val = next(bar, "default")

If you really want to do this in the iterator, you can use itertools.chain to chain your iterator with an infinite sequence of your sentinel value:

from itertools import chain, repeat
bar = chain(foo, repeat("default"))

To skip over elements inside a for loop, you could:

bar = iter(foo)
for val in bar:
if_some_condition():
next(bar) # skips over the next iteration of the loop

Default value for next element in Python iterator if iterator is empty?

next accepts a default value:

next(...)
next(iterator[, default])

Return the next item from the iterator. If default is given and the iterator
is exhausted, it is returned instead of raising StopIteration.

and so

>>> print next(i for i in range(10) if i**2 == 9)
3
>>> print next(i for i in range(10) if i**2 == 17)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>> print next((i for i in range(10) if i**2 == 17), None)
None

Note that you have to wrap the genexp in the extra parentheses for syntactic reasons, otherwise:

>>> print next(i for i in range(10) if i**2 == 17, None)
File "<stdin>", line 1
SyntaxError: Generator expression must be parenthesized if not sole argument

C++ using default-initialized iterators to create an empty std::string

According to cppreference.com, a random access iterator, of which a string iterator is one, meets all requirements of a bidirectional iterator.

Furthermore, a bidirectional iterator meets all requirements of a forward iterator.

Finally, since C++14, a forward iterator can be value-initialized, and will compare equal to all other value-initialized forward iterators of the same type:

A value-initialized LegacyForwardIterator behaves like the
past-the-end iterator of some unspecified empty container: it compares
equal to all value-initialized LegacyForwardIterators of the same
type.

Based on that, I believe this is well-defined at least as of C++14.

How to check if the iterator is initialized?

I managed to find this in the current standard (c++03 ). 24.1 p 5 tells :

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 container. 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. 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 exception is an assignment of a non-singular value to an iterator
that holds a singular value.
In this case the singular value is
overwritten the same way as any other value. Dereferenceable values
are always non- singular.

(Emphasis mine)

So the answer is : no, it is not possible.

Comparing default-constructed iterators with operator==

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.

NULL/default value for Template vector typename iterators

wish to initialize them to a fixed default value. In the case of pointers, I would have initialized to NULL, not sure what should be done for vector iterators.

There is no such thing for std::vector::iterator. Instances of std::vector::iterator are not sensible unless they are associated with a std::vector.

You can leave your std::vector::iterator default initialized but then you cannot use it unless it is set to a suitable value that is associated with an instance of std::vector.



Related Topics



Leave a reply



Submit