cbegin of the custom iterator does not get used

270 views Asked by At

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(); }
1

There are 1 answers

0
Nicol Bolas On BEST ANSWER

Range-based for never calls cbegin/cend. That's why you still need const versions of begin/end that return const_iterators.