c++c++11vectorstl

C++ iterate over iterator


I am currently "modernizing" my toy-compiler code to use as many C++11 features as possible to learn how to write modern C++. While looking at my code, searching for things to modernise I found this construct:

for (size_t i = 0; i < _vector.size(); i++) {
   for (size_t j = 0; j < _vector[i].length(); j++) {

_vector is a std::vector<std::string> containing the source code of the file which needs to be compiled. Each std::string in the vector is a line of code which needs to be compiled. Before compilation takes place the compiler checks whether the code to be compiled has any syntax errors, and the first step of this check is to look out for missing semicolons. I do this by looking at each character one by one and checking if said character is a semicolon. If it is, I perform a check whether the semicolon is needed by building a kind of mini-syntax-tree and thereby determining if it is necessary. I currently access each character with _vector[i][j] which is very reminiscent of ANSI-C (this is how you access each character in an array of char*) and I want to replace this with iterators. As far as I know, an Iterator is a pointer-like construct which points to an element in a container/sequence/whatever. Applying the pointer analogy I deduced that an std::vector<std::string>::iterator points to a string in a vector, therefore a theoretical std::vector<std::string>::iterator::iterator would point to a single character in a string object. But because there isn't such thing in the STL the pointer analogy isn't of much use here. So my question is: How do I access each Character within a std::vector<std::string>::iterator?


Solution

  • An iterator behaves like a pointer. So in your case, you can dereference the first iterator to get the string and use a std::string::iterator to access each character.

    Another way is to use operator->() to directly get at the std::string::iterator, e.g.

    for (std::vector<std::string>::iterator i = _vector.begin(); i != _vector.end(); ++i) {
        for (std::string::iterator j = i->begin(); j != i->end(); ++j) {
            /* ... */
        }
    }
    

    With C++11, you can simplify this to

    for (auto i = _vector.begin(); i != _vector.end(); ++i) {
        for (auto j = i->begin(); j != i->end(); ++j) {
            /* ... */
        }
    }
    

    As @LightnessRacesinOrbit mentioned and @MateuszGrzejek already showed, the next step would be moving to a Range-based for loop. A loop iterating from the first begin() to the last end() element

    for (auto i = _vector.begin(); i != _vector.end(); ++i) {
        // Do something with string (*i)
    

    can be written as

    for (auto &s : _vector) {
        // Do something with string (s)
    

    or, if you don't modify string, make it const

    for (const auto &s : _vector) {
        // Do something with string (s)