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?
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.
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):
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.
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With