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

Viewed 70

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?

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

Related