tags:

views:

388

answers:

4

When using C++ STL containers, under what conditions must reference values be accessed? For example are any references invalidated after the next function call to the container?

{
std::vector<int> vector;
vector.push_back (1);
vector.push_back (2);
vector.push_back (3);

vector[0] = 10;       //modifies 0'th element

int& ref = vector[0];
ref = 10;             //modifies 0'th element

vector.push_back (4);
ref = 20;             //modifies 0'th element???

vector.clear ();
ref = 30;             //clearly obsurd
}

I understand that in most implementations of the stl this would work, but I'm interested in what the standard declaration requires.

--edit: Im interested becuase I wanted to try out the STXXL (http://stxxl.sourceforge.net/) library for c++, but I realised that the references returned by the containers were not persistent over multiple reads, and hence not compatible without making changes (however superficial) to my existing stl code. An example:

{
std::vector<int> vector;
vector.push_back (1);
vector.push_back (2);


int& refA = vector[0];
int& refB = vector[1]; //refA is not gaurenteed to be valid anymore
}

I just wanted to know if this meant that STXXL containers where not 100% compatible, or indeed if I had been using STL containers in an unsafe/implementation dependant way the whole time.

+1  A: 

I expect that references would be invalidated only by any explicit or implicit resize() (see also the max_size, capacity, and reserve methods).

ChrisW
Agree. Why would clear() invalidate the memory? It would simply set it to null or similar.
Hooked
@Hooked: It's unportable to rely on that behaviour. A C++ standard library implementation is free to do whatever it wants, provided it obeys the semantic rules laid out in the 2003 C++ ISO standard.
j_random_hacker
+1  A: 

Vector will invalidate its iterator and references when it reallocates, which depends upon its current capacity. Although the above code might work in some cases, you shouldn't rely on this as the reference might be invalidated after the push_back(4) call.

Naveen
+7  A: 

Some basic rules for vector:

  • Reallocation invalidates all references, pointers, and iterators for elements of the vector.
  • Insertions may invalidate references, pointers, and iterators.
  • Inserting or removing elements invalidates references, pointers, and iterators that refer to the following elements.
  • If an insertion causes reallocation, it invalidates all references, iterators, and pointers.
aJ
* If you have reserved enough space, then a reallocation is guaranteed not to occur on insertion (probably obvious, but important).
Steve Jessop
+10  A: 

About inserting into vectors, the standard says in 23.2.4.3/1:

[insert()] causes reallocation if the new size is greater than the old capacity. If no reallocation happens, all the iterators and references before the insertion point remain valid.

(Although this in fact this talks about insert(), Table 68 indicates that a.push_back(x) must be equivalent to a.insert(a.end(), x) for any vector a and value x.) This means that if you reserve() enough memory beforehand, then (and only then) iterators and references are guaranteed not to be invalidated when you insert() or push_back() more items.

Regarding removing items, 23.2.4.3/3 says:

[erase()] invalidates all the iterators and references after the point of the erase.

According to Table 68 and Table 67 respectively, pop_back() and clear() are equivalent to appropriate calls to erase().

j_random_hacker
damn, I guess it is not possible to implement 100% STL compatible implementations of disk backed containers.
Akusete
I guess not, if you want to allow references to be freely made and held onto. But to my mind, keeping long-term references to items that live in another data structure is an "iffy" practice -- is there a reason why you can't just keep iterators instead?
j_random_hacker
If I want to get make multiple changes to a struct in a container, I often (possibly in bad practice) get a reference of it, then modify the reference. It is more effiecent than accessing it through the container each time (especially with tree based containers) and neater than taking a local copy and writing back afterwards. ... becuase its such a habbit, and was to the best of my knowledge a valid use of references, I was weary of using a library which that use would cause serious errors (which would also be un-detectable)
Akusete
I think the approach you described is fine for "small, local" changes, if you can be sure that the object referenced will not change (hopefully STXXL provides some weak guarantees about this). But using iterators to access items will not be much slower than using references, even for tree-based containers, at least on any modern optimising compiler and sensible STL implementation where tree nodes store values in-place -- all that's needed to get the address of the stored value is to add a constant to the node address (which is the iterator), and even this can usually be optimised away.
j_random_hacker