Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the following custom iterator implementation not play nice with `std::all_of`?

Tags:

c++

iterator

I am working on some iterator type that needs to wrap another iterator. For some reason, the custom iterator is not well defined. For example, it does not compile when used with std::all_of, complaining about a mismatching function call to std::iterator_category:

/opt/.../stl_algo.h:108:32: error: no matching function for call to '__iterator_category(Iterator<__gnu_cxx::__normal_iterator<int*, std::vector<int> > >&)'
108 |        std::__iterator_category(__first));

The custom iterator exposes a public iterator_category type, so I am not sure what the problem is here. To demonstrate the issue, the custom iterator simply wraps some other iterator type:

#include <functional>
#include <iterator>
#include <vector>

template<typename It>
struct Iterator
{
  using difference_type = typename std::iterator_traits<It>::difference_type;
  using value_type = typename std::iterator_traits<It>::value_type;
  using pointer = value_type*;
  using reference_type = value_type&;
  using iterator_category = std::input_iterator_tag;

  Iterator(It it) : it_{it} {}

  friend bool operator==(const Iterator& x, const Iterator& y) { return x.it_ == y.it_; }
  friend bool operator!=(const Iterator& x, const Iterator& y) { return !(x == y); }

  Iterator& operator++()
  {
    ++it_;
    return *this;
  }
  Iterator operator++(int)
  {
    Iterator it{*this};
    this->operator++();
    return it;
  }

  reference_type operator*() { return *it_; }

private:
  It it_;
};

int main()
{
  std::vector<int> v{1, 2, 3};

  auto first{Iterator{v.begin()}};
  auto last{Iterator{v.end()}};
  std::all_of(first, last, [](auto) { return true; });
}

The compiler errors are solved in case the iterator derives from std::iterator:

struct Iterator : public std::iterator<std::input_iterator_tag, typename It::value_type>

(see: https://godbolt.org/z/vMfj38)

So std::iterator brings something to the table that is missing from the definition of Iterator above, but what?

like image 325
Ton van den Heuvel Avatar asked Nov 14 '20 13:11

Ton van den Heuvel


People also ask

Should we stop using std:: iterator in C++?

std::iterator is deprecated, so we should stop using it. Indeed, the next step after deprecation could be total removal from the language, just like what happened to std::auto_ptr. But contrary to std::auto_ptr, the alternative to std::iterator is trivial to achieve, even in C++03: just implement the 5 aliases inside of your custom iterators.

What is the job of STD:: iterator?

The job of std::iterator is to expose those types. Here is one possible implementation of std::iterator: std::iterator allows an iterator to define this 5 types, by inheriting from std::iterator and passing it those types (at least the first 2 since the other 3 have default values):

How is STD:: iterator_traits implemented in GCC?

If you peek into libstdc++, used by gcc, you’ll see that std::iterator_traits is implemented as: This implies that, as soon as you try to access one member, such as ::iterator_category for example, the whole structured and all its typedefs are instantiated.

Can iterator be implemented as an inner class?

Note: The Iterator class can also, be implemented as an inner class of the Data Structure class since it won’t be used elsewhere. How next () and hasNext () work? To implement an Iterator, we need a cursor or pointer to keep track of which element we currently are on.


1 Answers

std::iterator_traits requires a reference member type, not reference_type. Fixing that allows it to compile successfully:

using reference/*_type*/ = value_type&;
...
reference/*_type*/ operator*() { return *it_; }

https://godbolt.org/z/PzK9fq

like image 50
Patrick Roberts Avatar answered Nov 01 '22 00:11

Patrick Roberts