What is a clean / concise way to pass the empty scope to a function taking a pair of iterators?


I have a function that takes a pair of iterators. I'd like to provide a no-argument version of the function that behaves as if it had been passed an empty range.

To be concrete, let's say the first function is:

void f(vector<int>::iterator b, vector<int>::iterator e) { // impl. }

I'd like to write this:

void f() { f({}, {}); }

Do I have the initialisation correct here, the {}, {} should be two default constructed vector::iterator types? (It compiles).

Do I have to construct a container to get a pair of iterators that compare equal?

In my understanding this cannot be done generally in a standard conforming way.


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; the only exceptions are destroying an iterator that holds a singular value, the assignment of a non-singular value to an iterator that holds a singular value, and, for iterators that satisfy the DefaultConstructible requirements, using a value-initialized iterator as the source of a copy or move operation. [ Note: This guarantee is not offered for default initialization, although the distinction only matters for types with trivial default constructors such as pointers or aggregates holding pointers. — end note ] In these cases the singular value is overwritten the same way as any other value. Dereferenceable values are always non-singular.

Default/value initialisation clearly isn't associated with any sequence, so the Iterator is singular. Comparing two singular Iterators is undefined behaviour.

std::vector<int>::iterator i;
std::vector<int>::iterator j = i; // UB, assignment of a non-a singular value
                                  // If you pass `i` to a function, a copy is done
                                  // which also results in UB (as in Andy's answer).

Next try, use value-initialisation

std::vector<int>::iterator i{};
std::vector<int>::iterator j = i; // ok, i is value-initialized
i == j; // undefined, comparison is not explicitly non-undefined behavour
i == i; // undefined, comparison is not explicitly non-undefined behavour

Since we don't have more information about vector<int>::iterator, your approach is wrong.

What you can do is changing your f to a template and use a pointer:

template <typename Iter>
void f(Iter b, Iter e);

int *p; f(p, p); // ok, defined for pointers