32
std::map<std::string, std::string> myMap;

std::map<std::string, std::string>::iterator i = m_myMap.find(some_key_string);
if(i == m_imagesMap.end())
    return NULL;

string *p = &i->first;

Is the last line valid? I want to store this pointer p somewhere else, will it be valid for the whole program life? But what will happen if I add some more elements to this map (with other unique keys) or remove some other keys, won’t it reallocate this string (key-value pair), so the p will become invalid?

michael
  • 3,250
  • 10
  • 43
  • 58
  • 2
    short answer: probably NO, long answer: it doesn't compile unless you previously used `typedef const std::string string;` instead of `using namespace std;` which is pretty confusing... – pqnet Nov 04 '11 at 09:33

4 Answers4

60

Section 23.1.2#8 (associative container requirements):

The insert members shall not affect the validity of iterators and references to the container, and the erase members shall invalidate only iterators and references to the erased elements.

So yes storing pointers to data members of a map element is guaranteed to be valid, unless you remove that element.

Greg Rogers
  • 35,641
  • 17
  • 67
  • 94
  • 2
    Actually, not quite ... the /iterator/ is stable, but keeping an iterator stable doesn't imply keeping the pointer stable (you could imagine a garbage collector making sure every iterator trace the data, but not pointers). – PierreBdR Feb 05 '09 at 14:35
  • Hmmmm, looks like you are right. While the standard explicitly makes notes about vector, list, and deque - when references and iterators are invalidated, I can't find anything in there about map/set iterator/reference validity. – Greg Rogers Feb 05 '09 at 15:20
  • Greg, iterators keep being valid, even if you erase other iterators. only the iterator that is being erased is invalidated by such an operation. i believe (though i have no looked up) data is not moved around in a map. would be nice if someone finds a quote on this - or a quote proving it wrong. – Johannes Schaub - litb Feb 06 '09 at 03:31
  • 5
    yeah, Greg, we are right. 23.1.2/8: "The insert members shall not affect the validity of iterators and references to the container, and the erase members shall invalidate only iterators and references to the erased elements." you get +1 by me, of course. – Johannes Schaub - litb Feb 06 '09 at 03:41
  • Thanks for the reference. I don't have a copy of the actual standard, and the draft standard on the web doesn't have this. The C++0x draft standard does have this in 23.1.4#8 – Greg Rogers Feb 06 '09 at 14:17
  • I've just told a crowd of people to upvote this answer. Let's hope they make it bubble up :) – Johannes Schaub - litb Jul 01 '09 at 14:30
  • 1
    The standard wording is a bit weird here, talking about "references to the container"... Also, for C++11 and the unordered containers, the intent is similar, and wording is cleaner: The insert and emplace members shall not affect the validity of references to container elements, but may invalidate all iterators to the container. The erase members shall invalidate only iterators and references to the erased elements. – PlasmaHH Dec 20 '13 at 20:37
  • 3
    Just for note: The text has been moved to new section p.744, 23.2.4#9 in newer version of standard draft. [N3797, 2013-10-13](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3797.pdf) – eonil Jan 28 '14 at 14:45
21

First, maps are guaranteed to be stable; i.e. the iterators are not invalidated by element insertion or deletion (except the element being deleted of course).

However, stability of iterator does not guarantee stability of pointers! Although it usually happens that most implementations use pointers - at least at some level - to implement iterators (which means it is quite safe to assume your solution will work), what you should really store is the iterator itself.

What you could do is create a small object like:

struct StringPtrInMap
{
  typedef std::map<string,string>::iterator iterator;
  StringPtrInMap(iterator i) : it(i) {}
  const string& operator*() const { return it->first; }
  const string* operator->() const { return &it->first; }
  iterator it;
}

And then store that instead of a string pointer.

Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
PierreBdR
  • 42,120
  • 10
  • 46
  • 62
  • Thanks, but I need to pass a pointer as windows message LPARAM, can I cast iterator to LPARAM and then back to iterator again? – michael Feb 05 '09 at 14:24
  • No, but you can store the iterator and then use &i->first, which is the string* you need. As Pierre says, this should be the most portable and fail-safe solution. – Gorpik Feb 05 '09 at 14:31
  • 10
    Hmm .. just looking at the other highly upvoted answer. Since the std (also) requires *references* to be stable, this would mean also pointers, not only iterators, are required to be stable. – Martin Ba Nov 16 '12 at 10:00
1

If you're not sure which operations will invalidate your iterators, you can look it up pretty easily in the reference. For instance for vector::insert it says:

This effectively increases the vector size, which causes an automatic reallocation of the allocated storage space if, and only if, the new vector size surpases the current vector capacity. Reallocations in vector containers invalidate all previously obtained iterators, references and pointers.

map::insert on the other hand doesn't mention anything of the sort.

As Pierre said, you should store the iterator rather than the pointer, though.

drby
  • 2,619
  • 25
  • 26
  • 2
    cplusplus.com is a bad source for quoting that kind of stuff. In general, examples of cplusplus.com are poor. I'm not saying that just because of fun, but i know it - i've seen it many times. best example of its poor quality is http://www.cplusplus.com/reference/iostream/ostream/operator%3C%3C.html – Johannes Schaub - litb Feb 06 '09 at 03:39
  • nothing regarding your answer though. just to tell you what i think about that site. – Johannes Schaub - litb Feb 06 '09 at 03:40
  • So what do you use for a reference if you don't have a book handy? www.cppreference.com still feels incomplete. – drby Feb 06 '09 at 08:21
  • hackingwords. i often end up looking at cplusplus.com for quick reference on function names. for "stl", i look into the SGI site, which i find quite good. they don't document c++ streams and others that wasn't in the SGI stl, so i look into cplusplus.com for those. – Johannes Schaub - litb Feb 06 '09 at 21:51
  • but i would not recommend you to trust its examples or even learn from it. i've seen too many cases where it oversimplificates things or even is wrong (like in my example, they say operator<< takes references-to-nonconst (copied from operator>>?).and they keep saying functions in std:: are global). – Johannes Schaub - litb Feb 06 '09 at 21:53
  • http://www.sgi.com/tech/stl/ for reference. have fun. wg21 is http://www.open-std.org/jtc1/sc22/wg21/ here. last working paper (c++1x though) http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2800.pdf . you can find c++98 drafts too, of course. – Johannes Schaub - litb Feb 06 '09 at 21:56
  • of course i'm not alone with what i think about cplusplus.com and similar sites. people from ##c++ irc channel at freenode told me, and i've seen they are right over time. anyway, as i say i still use it for quick references. hope this helps somewhat :) have fun – Johannes Schaub - litb Feb 06 '09 at 21:57
0

Why are you wanting to do this?

You can't change the value of *p, since it's const std::string. If you did change it, then you might break the invariants of the container by changing the sort order of the elements.

Unless you have other requirements that you haven't given here, then you should just take a copy of the string.

Richard Wolf
  • 4,039
  • 1
  • 21
  • 14