So I have written a custom iterator for std::map
that allows access to just the "values" of the map and I was wondering if it is the "correct" way to implement std::iterator
(acts identically to std::iterator
).
Here is the implementation I have written:
template<typename K, typename V>
class MapValueIterator : public std::iterator<std::forward_iterator_tag, V> {
public:
MapValueIterator();
MapValueIterator(typename std::map<K, V>::iterator iterator) : iterator(iterator) { }
MapValueIterator(const MapValueIterator<K, V> &iterator) : iterator(iterator.iterator) { }
MapValueIterator<K, V> &operator=(const MapValueIterator<K, V> &iterator) {
this->iterator = iterator.iterator;
}
bool operator==(const MapValueIterator<K, V> &iterator) const {
return this->iterator == iterator.iterator;
}
bool operator!=(const MapValueIterator<K, V> &iterator) const {
return this->iterator != iterator.iterator;
}
V &operator*() const {
return (iterator->second);
}
V *operator->() const {
return &(iterator->second);
}
MapValueIterator<K, V> &operator++() {
++iterator;
return *this;
}
MapValueIterator<K, V> operator++(int) {
return (*this)++;
}
private:
typename std::map<K, V>::iterator iterator;
};
And an example of how it can be used:
std::map<int, int> values;
values.insert(std::make_pair(0, 1));
values.insert(std::make_pair(1, 2));
values.insert(std::make_pair(2, 5));
MapValueIterator<int, int> begin(values.begin());
MapValueIterator<int, int> end(values.end());
for (auto iterator = begin; iterator != end; ++iterator) {
printf("%d\n", *iterator);
}
2 Answers 2
It looks about perfect.
You got the post increment wrong though:
MapValueIterator<K, V> operator++(int) {
return (*this)++;
}
I believe this goes into an infinite recursion.
Thus this is normally implemented as:
MapValueIterator<K, V> operator++(int) {
MapValueIterator<K, V> result(*this); // get a copy for return
// so this can be used
// unaltered in the expression
// Now implement the current object.
++(*this);
// Returned the saved copy.
return result;
}
The only difference is that the std::map<K,V>::iterator
type actually implements the Bidirectional iterator
concept. The question is why is your iterator only implement the Forward iterator
concept.
I have one other addition to what Loki has already pointed out.
The C++ committee has voted to deprecate inheriting from std::iterator
as of C++17. The current recommendation is apparently to write those typedef
s yourself.
template<typename K, typename V>
class MapValueIterator {
public:
typedef V value_type;
typedef std::ptrdiff_t difference_type;
typedef V *pointer;
typedef V &reference;
typedef std::ForwardIteratorTag iterator_category;
// ...
Feel free to read the proposal to deprecate it, if you care about why they're doing it (and such).
-
1\$\begingroup\$ Just to clarify something for future readers.
std::iterator
is not getting deprecated, only the practice of inheriting fromstd::iterator
in your custom iterator class has been deprecated. \$\endgroup\$KeyC0de– KeyC0de2019年10月02日 22:58:23 +00:00Commented Oct 2, 2019 at 22:58 -
\$\begingroup\$ Shouldn't one prefer
using value_type = V;
instead? \$\endgroup\$KcFnMi– KcFnMi2024年01月25日 17:04:09 +00:00Commented Jan 25, 2024 at 17:04 -
\$\begingroup\$ @KcFnMi: Some people prefer them, certainly. I do in some cases, but not others. \$\endgroup\$Jerry Coffin– Jerry Coffin2024年01月26日 05:08:29 +00:00Commented Jan 26, 2024 at 5:08
unordered_map
or amultimap
. You could arguably use a template-template parameter in this case. \$\endgroup\$unordered_map
andmap
have differing numbers of template parameters and all of the parameters are required when defining the template (i.e.template<template<class, class, class, class> class M, typename K, typename V>
only works formap
whiletemplate<template<class, class, class, class, class> class M, typename K, typename V>
only works forunordered_map
(note the extra class in the template). \$\endgroup\$template<template<class...> M, typename K, typename V>
which works perfectly! \$\endgroup\$M
. Then the constructor parameter isM::iterator
and the value type can be retrieved fromM::mapped_type
. \$\endgroup\$