Preparation for Std::Iterator Being 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).

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;
...

Is this an acceptable way of making an iterator?

I would do this in two parts.

One is a range class that just acts as a wrapper for a stream iterators:

template <class T>
class istream_range {
std::istream_iterator<T> b;
std::istream_iterator<T> e;
public:
istream_range(std::istream &is)
: b(std::istream_iterator<T>(is))
, e(std::istream_iterator<T>())
{}

std::istream_iterator<T> begin() { return b; }
std::istream_iterator<T> end() { return e; }
};

So this lets use use istream_iterators in a range-based for loop:

for (auto const &s : istream_range<foo>(myfile))
// do something with s

An istream_iterator uses operator>> to extract items from the specified file, so the second part is just a tiny type that extracts a line:

class line {
std::string data;
public:
friend std::istream &operator>>(std::istream &is, line &l) {
std::getline(is, l.data);
return is;
}
operator std::string() const { return data; }
};

So, with this our for loop becomes something like:

for (auto const &s : istream_range<line>(myfile))
// do something with s

The obvious advantage of this is decoupling the two: we can use istream_range<T> to process a file of T, for any T that normal stream extraction does "the right thing" (including lots of custom extractors of which we can't currently have any awareness).

There are a few more possibilities covered in the answers to a previous question (including a LineInputIterator that seems to be a little closer to what you're asking for).

How to implement standard iterators in class

A range-based for loop only requires that your class have begin() and end() methods (or overloads of std::begin() and std::end()) that return iterators. It doesn't care where those iterators come from. So, the simplest solution is to just use the array's own iterators instead of trying to define your own, eg:

template <typename T>
class Vec_3D
{
public:
typedef typename std::array<T, 3> array_type;
typedef typename array_type::iterator iterator;
typedef typename array_type::const_iterator const_iterator;
// or:
// using array_type = std::array<T, 3>;
// using iterator = array_type::iterator;
// using const_iterator = array_type::const_iterator;
...

inline iterator begin() noexcept { return vec.begin(); }
inline const_iterator cbegin() const noexcept { return vec.cbegin(); }
inline iterator end() noexcept { return vec.end(); }
inline const_iterator cend() const noexcept { return vec.cend(); }
...

private:
array_type vec;
};

std::inserter replacement for C++17

std::inserter is no longer available with c++17

That's not true!

There is no indication that std::inserter will be removed from C++17, or that it will become deprecated.


I was getting confused with std::iterator

Preparation for std::iterator Being Deprecated.

Correct type declaration for method returning iterator to C array

std::iterator is meant to be used as a base class. From 24.4.2/1:

namespace std {
template<class Category, class T, class Distance = ptrdiff_t,
class Pointer = T*, class Reference = T&>
struct iterator {
typedef T value_type;
typedef Distance difference_type;
typedef Pointer pointer;
typedef Reference reference;
typedef Category iterator_category;
};
}

It only gives you some typedefs and won't magically implement all the required operators. In your case, begin() and end() should probably just return char*, which already has specializations for std::iterator_traits.

However, if your iterator has to be smarter (maybe this is some sort of circular buffer, for example), you would have to create your own iterator class and implement the required operators. For that iterator to work with various features in the standard library (such as std::iterator_traits), you'll need predefined typedefs such as value_type, iterator_category, etc.

Since getting those right can sometimes be tricky, std::iterator will define those for you based on the given template parameters. Here's an example of using std::iterator_traits with an iterator.

Note that as of C++17, std::iterator has been deprecated for various reasons. This post has some workarounds.



Related Topics



Leave a reply



Submit