Why Is Std::Iterator Deprecated

Why is std::iterator deprecated?

From the proposal that suggested its deprecation:

As an aid to writing iterator classes, the original standard library supplied the iterator class template to automate the declaration of the five typedefs expected of every iterator by iterator_traits. This was then used in the library itself, for instance in the specification of std::ostream_iterator:

template <class T, class charT = char, class traits = char_traits<charT> >
class ostream_iterator:
public iterator<output_iterator_tag, void, void, void, void>;

The long sequence of void arguments is much less clear to the reader than simply providing the expected typedefs in the class definition itself, which is the approach taken by the current working draft, following the pattern set in C++14 where we deprecated the derivation throughout the library of functors from unary_function and binary_function.

In addition to the reduced clarity, the iterator template also lays a trap for the unwary, as in typical usage it will be a dependent base class, which means it will not be looking into during name lookup from within the class or its member functions. This leads to surprised users trying to understand why the following simple usage does not work:

#include <iterator>

template <typename T>
struct MyIterator : std::iterator<std::random_access_iterator_tag, T> {
value_type data; // Error: value_type is not found by name lookup

// ... implementations details elided ...
};

The reason of clarity alone was sufficient to persuade the LWG to update the standard library specification to no longer mandate the standard iterator adapators as deriving from std::iterator, so there is no further use of this template within the standard itself. Therefore, it looks like a strong candidate for deprecation.

You can also see STL's reasoning in LWG 2438. (h/t T.C.)


As for some other way of doing it, not really. You could basically implement your own version of std::iterator (which isn't too hard) or manually write out all of those typedefs (which isn't too hard either, and I actually prefer it for clarity).

Why do *_iterators still require typedef something void after removing std::iterator?

Because ISO C++17 std::iteartor_traits needs all the 5 of these member types in general, even if it is already SFINAE-friendly since C++17. If there are insufficient nested types, the instance of std::iterator_traits will have no members desired. Currently, this is not changed in the current draft.

See also iterator_traits SFINAE friendliness for rationale of the original proposal.

What is the design purpose of iterator_traits?

It seemed that in general, the iterator_traits just extracts properties that defined explicitly in iterator. If a iterator_traits is to be used, one can use the corresponding iterator directly.

Not all iterators can type aliases as members.

What's the benefit this indirection?

To allow all iterators to have a uniform interface. More specifically, pointers are iterators too and as non-class types, pointers cannot have members.

So, when for example the user of an iterator wants the answer to the question "What is the type of the object pointed by an_iterator_type", they cannot use an_iterator_type::value_type because that cannot be defined for all iterators - specifically pointers. What they can use is std::iterator_traits<an_iterator_type>::value_type which can be defined for all iterators - including pointers. That is the purpose of std::iterator_traits.


The standard defines the std::iterator_traits<T*> specialisations for pointers. Although you can, you don't need to define specialisations for custom iterator types because you can instead define the types as members, which will be used by the generic definition of std::iterator_traits.

The intention of std::iterator was to be optionally used as a base class to help define the member types for custom iterators. However its use was never necessary, it is considered to be a flawed design and its use has long been discouraged. Since C++17, it has been deprecated and it may be removed from future standards.

An example that hopefully demonstrates the problem of the design of std::iterator:

// using the deprecated std::iterator
struct iterator : std::iterator<
std::input_iterator_tag, // OK so far
long, // can you guess what member this argument defines?
long, // how about this?
const long*, // you can probably guess this one
long> // still, no clue
{
...

// recommended way without using std::iterator
struct iterator
{
using iterator_category = std::input_iterator_tag;
using value_type = long;
using difference_type = long;
using pointer = const long*;
using reference = long;
...

C++ iterator to string-like types

std::iterator is not meant to be used that way. It was introduced into the library to support user-defined iterators by providing certain member types like iterator_category and value_type. std::iterator is an empty class and taking it by value is meaningless. Taking it by reference doesn't make much sense either, because iterators do not have to be derived from it. std::iterator is deprecated since C++17.

LegcayInputIterator is not a class, but a collection of requirements a type should satisfy. The type itself is not fixed and should be a template parameter. Since C++20, when concepts will become a language feature, you'll be able to write something like this:

template<typename InputIt> requires InputIterator<InputIt>
Example(It begin, It end) { ... }

or

template<InputIterator InputIt>
Example(It begin, It end) { ... }

to check that InputIt indeed satisfies the requirements of an input iterator. Here InputIterator is a concept, see here for a particular example. Until then you simply write:

template<typename InputIt>
Example(InputIt begin, InputIt end) { ... }

For example, std::vector's constructor from a range [first, last) is declared this way:

template<class InputIt>
vector(InputIt first, InputIt last, const Allocator& alloc = Allocator());


Related Topics



Leave a reply



Submit