I have defined two iterators over keys of a map:
template<class MyMap>
struct MapKeyIterator : MyMap::iterator {
using Base = typename MyMap::iterator;
using Key = typename MyMap::key_type;
MapKeyIterator() : Base(){};
MapKeyIterator(Base it_) : Base(it_){};
Key *operator->() const { return &(Base::operator->()->first); }
Key operator*() const { return Base::operator*().first; }
};
template<class MyMap>
struct MapKeyConstIterator : MyMap::const_iterator {
using Base = typename MyMap::const_iterator;
using Key = typename MyMap::key_type;
MapKeyConstIterator() : Base(){};
MapKeyConstIterator(Base it_) : Base(it_){};
Key *operator->() const { return &(Base::operator->()->first); }
Key operator*() const { return Base::operator*().first; }
};
The following type uses these iterators:
struct A {
using MyMap = std::map<int, int>;
using KeyIterator = MapKeyIterator<MyMap>;
using KeyConstIterator = MapKeyConstIterator<MyMap>;
KeyIterator begin() { return m.begin(); }
KeyConstIterator cbegin() const { return m.cbegin(); }
KeyIterator end() { return m.end(); }
KeyConstIterator cend() const { return m.cend(); }
private:
MyMap m{{1, 2}, {2, 4}, {3, 6}, {4, 8}};
};
The following does not compile:
void f(const A &a) {
for (const auto &el: a)
std::cout << el << std::endl;
}
int main() {
A a;
f(a);
return 0;
}
The error message by g++ 5.4.0 reveals that begin
is used instead of cbegin
. Why?
temp.cpp: In function ‘void f(const A&)’:
temp.cpp:68:26: error: passing ‘const A’ as ‘this’ argument discards qualifiers [-fpermissive]
for (const auto &el: a)
^
temp.cpp:59:17: note: in call to ‘A::KeyIterator A::begin()’
KeyIterator begin() { return m.begin(); }
^
temp.cpp:68:26: error: passing ‘const A’ as ‘this’ argument discards qualifiers [-fpermissive]
for (const auto &el: a)
^
temp.cpp:61:17: note: in call to ‘A::KeyIterator A::end()’
KeyIterator end() { return m.end(); }
Range-based
for
never callscbegin
/cend
. That's why you still needconst
versions ofbegin
/end
that returnconst_iterator
s.