smallB
smallB

Reputation: 17120

Returning iterator seems to invalidate it

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?

Upvotes: 3

Views: 164

Answers (2)

Krizz
Krizz

Reputation: 11542

I assume, the actual code probably has templates (typename debris) and probably they are not referenced correctly in function argument of return type.

The following code works as expected:

const int some_position = 2; 

template <typename T>
typename T::const_iterator return_iterator(const T& line)
{
     typename T::const_iterator beg = line.begin(); 
     beg = line.begin() + some_position;
     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);
    std::cout << "character to 2: " << *pos << std::endl;
}

int main()
{
    std::string str = "Hello world";
    other_fnc(str);
}

Verify your code according to it. If you are doing something else, update your question.

(PS: I got rid of auto of C++11, as I don't have the compliant compiler at the moment)

Upvotes: 0

Daniel Trebbien
Daniel Trebbien

Reputation: 39208

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.

Upvotes: 1

Related Questions