44

Iterators of the categories forward, bidirectional, and random access need to be default-constructible.

Why is this, and why do input and output operators not have to be default-constructible?

2
  • 4
    Probably because many used to write std::vector<T>::iterator it; outside of a for loop to avoid going over the recommended 78 characters line width. If you were to use std::vector<T>::iterator it = container.begin(); then the for loop would not be as sexy: for (; it != container.end(); ++it). Commented Mar 3, 2015 at 13:13
  • 3
    Unfortunately some algorithms are implemented in such a way to require default-constructible. This seems to be in principle a reasonable requirement to avoid premature assignment but in the examples I saw it can be avoided easily. I recently tried to implement an iterator and requiring default-constructible unfortunately added a level of indirection that was unnecessary otherwise. So I ended up paying for the extra level of indirection all the time, I think this requirement is very unfortunate because it has a nasty hidden cost. Commented Jun 12, 2018 at 20:40

3 Answers 3

19

Forward iterators and stronger are required to refer to some external sequence (see [forward.iterators]/6 which says "If a and b are both dereferenceable, then a == b if and only if *a and *b are bound to the same object.")

This means they are generally just a lightweight handle onto something else (e.g. a pointer to an element or a node in a container) and so there is little reason not to require that they can be default constructed (even if default construction creates a singular iterator that can't be used for anything until assigned a new value). It's possible for all non-pathological* forward iterators to support default construction, and relying on that makes some algorithms easier to implement.

Iterators which only meet the input iterator or output iterator requirements (and nothing stronger) might contain state within themselves which is modified by operator++ and so it might not be possible for that state to be default-constructed. No algorithm that only operates on input/output iterators needs to default construct them, so it isn't required.

* spot the "no true scotsman" argument here ;)

Sign up to request clarification or add additional context in comments.

8 Comments

The standard doesn't generally specify something just because there's no reason not to; they generally have a very good reason.
To me this seems very STL container-specific. It's easy to think of some custom iterator that stores more state than a std::vector<>::iterator and where that state cannot be created out of nothing either. And why shouldn't input and output iterator be singular? (In fact, stream iterators are default-constructible.)
@MarkRansom Note that the SGI STL says all iterators must be DefaultConstructible, the standard actually removed that requirement for input and output iterators.
In libstdc++ (and so probably also in the SGI STL) at least std::search, std::partition_point and std::minmax_element rely on default-constructing a forward iterator. std::rotate default constructs a bidirectional iterator. In theory they could be turned into redundant copies (they will be assigned new values later anyway).
I suspect that it happened the other way around, i.e. rather than intentionally requiring it only for forward and better it was "un-required" for input/output. The SGI STL authors, and maybe Stepanov too, relied on DefaultConstructible for all iterators, as generalisations of pointers. At some point during standardisation someone realised that no algorithms operating in input or output iterators actually needed default construction, so the requirement was dropped for those categories. Maybe it could also have been dropped for forward and bidirectional, if anyone had bothered to do the work.
|
4

reference for iterators

Input/Output iterators:

Input Iterator: once an InputIterator i has been incremented, all copies of its previous value may be invalidated.

Output Iterator: After this operation r is not required to be dereferenceable and any copies of the previous value of r are no longer required to be dereferenceable or incrementable.

If we look at this it seems clear that these iterators are designed to be used in the simplest method possible. Much like an array index or simple pointer would be used for single-pass algorithms. There is therefore really no need to have default constructors.

Note however that just because default constructors is not required technically doesn't disqualify you from implementing them if you want.

Forward iterators:

These are the first level of iterators that require default constructors but why?

There are many reasons related to historical programming reasons ect and I believe this is all to some extent valid. In a certain way I think the committee figured somewhere between iterator and randomAccessIterator default construction was required to be implemented and forward iterators seemed like the best choice.

There is however one fairly good reason why:

Forward iterators support multi-pass algorithms and therefore require that copies of the iterator are still valid after the iterator has been used/incremented. If these copies are valid still it means the algorithm would allow us to "save" them somewhere. Which also means the iterator where we save them needs to have a default/initial value.

Consider:

class MyClass
{
 public:
  void myFunction(ForwardIterator &i)
  {
    //do some code here
    savedIter = i;
    //do some code here
  }
 private:
  ForwardIterator savedIter;
}

By definition this is valid because we are allowed to save the iterator for some amount of time as the requirement is that copies of this iterator will remain valid. (At least until the data structure the iterator points to is destroyed)

However for this class to be created ForwardIterator requires a default constructor obviously...

4 Comments

You could save just as well copy-constructed iterators, couldn't you? All in all, there's a lot of hand waving in this answer.
Can you please explain how you would create a "placeholder" in which to save iterators without a default constructor in an elegant way? Also english is not my first language so I'm not 100% sure to which part you refer about "hand waving" can you please explain so I can try to improve the answer.
In my experience, hand waving often occurs where quotation marks are used, or words like "obviously".
You could very well use placement new. Default construction is not required.
3

Forward / bidirectional / random access iterators can often be pointers - it would historically have aided migration from pointer-using code to iterators if the construction and initialisation could be left delocalised if that was the way the code happened to be. Forcing more wholesale change would have frustrated a lot of people trying to migrate older code off explicit use of pointers and onto iterators. Changing it would now break a lot of code.

Input and output operators are often most elegantly implemented with references to underlying stream or other I/O objects, and references must be initialised at construction. Of course, implementations could be forced to defer that, and use pointers internally, but that's sure to rub some people up the wrong way - seeming too "C"-like - so it's unsurprising the Standard facilitates use of references.

3 Comments

This is more of an argument why they must be assignable. std::istream_iterator is default-constructible and the end-of-stream marker.
@MSalters: std::istream_iterator had a very specific use for default construction (as a sentinel), so it could prioritorise that and accept whatever implementation compromises that required, but the Standard's not requiring all input/output iterators to be default constructible still lets individual implementations make what they consider the most elegant choice.
I think this answer spots the dilemma, DefaultConstructible is a nice requirement, but forces you to implement things in terms of pointers, even if no pointers needed to be involved a priori, and penalizes an more elegant implementation in terms of references.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.