Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Returning iterator seems to invalidate it

Tags:

c++

iterator

I'm returning iterator from my fnc after I used it and this iterator at the point of returning points to some character but after this fnc returns it the returned iterator doesn't point to that character anymore. What am I doing wrong?

typename std::string::const_iterator return_iterator(const std::string& line)
{
    auto beg = line.begin();  
/*here I'm moving this iterator and assing to it i.e.*/  
    beg = line.begin() + some_position;
    return beg;//at this point it points to a character
}  

void other_fnc(const std::string& line)
{
auto pos = return_iterator(line);//after this fnc returns pos points to some rubbish
}

Any ideas?

like image 587
smallB Avatar asked May 15 '26 04:05

smallB


1 Answers

As the example code that you posted does not compile (typename std::string::const_iterator should be simply std::string::const_iterator), and fixing this error allows the code to run as expected, I am guessing that the actual code is a bit different.

This sounds to me like a case of a std::string parameter being copied by value and the reference counting being somehow disrupted before the const_iterator is accessed. For example, this signature:

std::string::const_iterator return_iterator(std::string line)
                                                   // ^ Notice: Not passing by
                                                   //           reference

passes the string by value. Because of COW, the copy is shared, but the moment that the function calls line.begin(), the non-const string member function std::string::begin() is called, meaning that a new copy of the underlying string is typically created. The non-const iterator that is returned is then converted implicitly to a const_iterator (a perfectly valid conversion).

EDIT: To demonstrate my point, take a look at the output of the following version that was modified to pass the line parameter to return_iterator() by value:

#include <cassert>
#include <string>
#include <iostream>

std::string::const_iterator return_iterator(std::string line)
{
    std::string::const_iterator beg = line.begin();
    std::cout << "In return_iterator(), &(*line.begin()) is " << static_cast<const void *>(&*beg) << '\n';
/*here I'm moving this iterator and assing to it i.e.*/
    beg = line.begin() + 3;
    return beg;//at this point it points to a character
}

void other_fnc(const std::string& line)
{
    std::string::const_iterator pos = return_iterator(line);//after this fnc returns pos points to some rubbish
    std::cout << "In other_fnc(), &(*line.begin()) is " << static_cast<const void *>(&*line.begin()) << '\n';
    assert(*pos == line[3]);
}

int main()
{
    std::string line = "This is a test.";
    other_fnc(line);
}

http://codepad.org/K9yaWqWA

The assertion now fails. Also, notice that the addresses of *line.begin() are different.

like image 92
Daniel Trebbien Avatar answered May 19 '26 03:05

Daniel Trebbien