STL iterator before std::map::begin()

☆樱花仙子☆ 提交于 2019-12-23 09:21:48

问题


In C++11's std::map, is there some valid iterator x such that ++x is guaranteed to equal map::begin()? I would like to detect if a function I just called (mine) has walked an iterator off the front of a function. The function will move the iterator exactly one position backward.

Does the answer hold for the rest of the library?


回答1:


No, iterators before the beginning in std containers are all UB (except for reverse iterators, which will probably not solve your problem).

You probably need to fix the function in question. Failing that, wrap it and catch the bad behavior before you call it. Failing that, you could insert a negative infinity element into the map key type ordering, and add a sentinal value. Failing that, you could write iterator adapters that wrap your map iterators with ones that can go one-before-beginning without UB.

These are ordered in my order of recommendation, roughly. Each has ways it could fail, and they get more error prone and dangerous as my recommendation gets more remote.




回答2:


It's very important to realize that Standard Library containers are semi-open ranges [begin, end), i.e. you can iterate one-past-the-end. For bidirectional (and random) iterators you can also do --end() and come back from the brink. Dereferencing one-past-the-end by *end() is undefined behavior, and so is decrementing the begin iterator by --begin() or begin() - 1. There is only one exception to this: std::forward_list which has a non-dereferenceable iterator before_begin() that satisfies ++before_begin() == begin() (but note that for a forward_list you cannot decrement begin() either).

This fundamental asymmetry for bidirectional iterators means that reverse iterators are thin wrappers around regular iterators. In most Standard Library implementations they simply contain a copy base_ of the underyling iterator. Incrementing a std::reverse_iterator calls something like --base_; return *this;, and dereferencing it does auto old = base_; return *--old;. At no point is the underlying iterator decremented to before begin(), and no dereferencing of end() is done that way.

Below are the four ways to iterate over a container supporting bidirectional or random iterators, and the relations between the various iterators (.base() converts a std::reverse_iterator back to its underlying iterator)

#include <iomanip>
#include <iostream>
#include <iterator>
#include <map>
#include <string>

int main()
{    
    auto c = std::map<int, std::string>{ {1, "hello"}, {2, "world"} };

    {   // 1) forward iteratation
        auto it = begin(c);
        for (; it != end(c); ++it){}
        std::cout << std::boolalpha << (it == c.rbegin().base()) << "\n";
    }

    {   // 2) meh, backward iteration
        auto it = end(c);
        for (; it != begin(c); --it){}
        std::cout << std::boolalpha << (it == c.rend().base()) << "\n";
    }

    {   // 2') better: reverse iteration
        auto it = c.rbegin();
        for (; it != c.rend(); ++it){}
        std::cout << std::boolalpha << (it.base() == begin(c)) << "\n";
    }

    {   // 1') backward reverse, better avoid this
        auto it = c.rend();
        for (; it != c.rbegin(); --it){}
        std::cout << std::boolalpha << (it.base() == end(c)) << "\n";
    }
}

Live Example

If you have data structure that should support bidirectional iteration but there are no member iterators .rbegin() or rend(), you can easily define them yourself by std::reverse_iterator(end()) and std::reverse_iterator(begin()), respectively (this is also the way the Standard Library usually implements them).




回答3:


By "walk the iterator off the front" I presume you are decrementing a forward iterator something like this:

// don't do this:
for(it = mymap.end(); --it >= mymap.begin(); ) { ... }

Instead, increment a reverse iterator like this:

// this is better:
for(it = mymap.rbegin(); it != mymap.rend(); ++it) { ... }

-Jesse



来源:https://stackoverflow.com/questions/19417171/stl-iterator-before-stdmapbegin

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!