Constant reference std::vector - c++

How can I have a reference to a specific element in a std::vector?
The easy way should be to store it as the index of the element (I am using a size_t variable).
The problem I have is the possibility of inserting an element before the current, making the stored value incorrect.

There isn't a very good way to keep track of a single element inside the vector by a pointer, reference, or index. In particular:
If you insert a new element into the vector, it may cause all outstanding references to the vector's elements to be invalidated if an internal reallocation occurs. Insertions can happen either by calling insert, reserve, push_back, or assign (plus perhaps a few others). This could cause the reference to refer to an invalid object, resulting in undefined behavior if the reference is used.
If you remove an element from the vector, then the reference may no longer point at the same element. Accessing the reference may result in you referring to the wrong object.
If you really must hold a reference to an element in a vector, one option would be to have the vector store (smart) pointers to objects instead of the objects themselves. That way, you can store a copy of that pointer elsewhere and no matter what happens in the vector, the pointer should still be valid. This is really the Fundamental Theorem of Software Engineering in practice - adding another layer of indirection can solve most problems.

The only reliable solution is to have your vector be a vector of pointers and just remember your pointer.
Otherwise you can't maintain a reference because, as you mentioned, the vector might move elements around.

Use a vector that contains auto pointers, i.e. std::vector<std::unique_ptr<YOUR_ELEMENT_TYPE>> if you are using c++0x.

Related

How to access element(having a unique identifier) in a vector using a map in constant time? [duplicate]

When vector needs more memory it will reallocate memory somewhere, I don't know where yet! and then pointers become invalid, is there any good explanation on this?
I mean where they go, what happen to my containers? ( not linked list ones )
Short answer: Everything will be fine. Don't worry about this and get back to work.
Medium answer: Adding elements to or removing them from a vector invalidates all iterators and references/pointers (possibly with the exception of removing from the back). Simple as that. Don't refer to any old iterators and obtain new ones after such an operation. Example:
std::vector<int> v = get_vector();
int & a = v[6];
int * b = &v[7];
std::vector<int>::iterator c = v.begin();
std::advance(it, 8);
v.resize(100);
Now a, b and c are all invalid: You cannot use a, and you cannot dereference b or c.
Long answer: The vector keeps track of dynamic memory. When the memory is exhausted, it allocates a new, larger chunk elsewhere and copies (or moves) all the old elements over (and then frees up the old memory, destroying the old objects). Memory allocation and deallocation is done by the allocator (typically std::allocator<T>), which in turn usually invokes ::operator new() to fetch memory, which in turn usually calls malloc(). Details may vary and depend on your platform. In any event, any previously held references, pointers or iterators are no longer valid (presumably because they refer to the now-freed memory, though it's not specified in the standard why they're invalid).
When you add or remove items from a vector, all iterators (and pointers) to items within it are invalidated. If you need to store a pointer to an item in a vector, then make the vector const, or use a different container.
It shouldn't matter to you where the vector stores things. You don't need to do anything, just let it do its job.
When you use std::vector, the class takes care of all the details regarding memory allocation, pointers, resizing and so on.
The vector class exposes its contents through iterators and references. Mutations of the vector will potentially invalidate iterators and references because reallocation may be necessary.
It is valid to access the contents using pointers because the vector class guarantees to store its elements at contiguous memory locations. Clearly any mutation of the list will potentially invalidate any pointers to its contents, because of potential reallocation. Therefore, if you ever access an element using pointers, you must regard those pointers as invalid once you mutate the vector. In short the same rules apply to pointers to the contents as do to references.
If you want to maintain a reference to an item in the vector, and have this reference be valid even after mutation, then you should remember the index rather than a pointer or reference to the item. In that case it is perfectly safe to add to the end of the vector and your index value still refers to the same element.

handling for references affected by adding elements to std::vector

As pointed out in answer to another question, all pointers to a vector's elements may become invalid after new elements have been added to that vector, due to reallocation of the underlying contiguous buffer.
Is there a safe way to handle this issue at compile-time?
Are there best-practices to deal with or to avoid a situation, where references may become invalid after altering the data-structure?
Are there best-practices to deal with or to avoid a situation, where references may become invalid after altering the data-structure?
preallocate enough space for vector in advance
keep index of object in array instead of reference/pointer to object itself
use different container, for example std::list
which way will work for you the best way depends on your situation
A pointer or reference to the std::vector itself won't change. What could change is the address of a specific element inside the std::vector due to reallocation policy which is implementation dependent.
Preallocating enough space is a risk because you shouldn't relay on a particular implementation policy.
Storing the address of an element in a std::vector is a bad idea and can be easily avoided because std::vector [] operator is very fast so store the index instead the address of the element. This the right way.

Container that guarantee fixed position for items

Is there C++ container that guarantees a fixed pointer for items what ever changes happened?
For example, std::vector may change the address of an item if a push_back or erase happened. So the address of the item will be rubbish after this change. So, is there a container that does not change items address in memory while the container changing?
P.S. compile time size or fixed size is not an option
EDIT:
As #Joachim Pileborg stated it is XY problem or in actual it is XYZ one! The Z is this question. The Y is the following one:
Keeping vector of iterators of the data
The original one:
I have data which is set of Points(x,y). This Points will go into a pipeline. The result should be:
set of Lines
set of Points for each line... in other word, set of set of Points
I do not want to copy the point and return them by value. I know a Point with just x and y is nothing to worry about copying it. However, in my it is templated problem which may be much bigger object in some cases.
Is there C++ container that guarantees a fixed pointer for items what ever changes happened?
If by what ever you include erasing the item that is pointed to, then only std::array is such container because you cannot remove elements from it.
If you mean that anything else but erasing the pointed item, then all node based containers have that property, as pointed out in the comments. Such standard containers are std::list, std::forward_list, std::map std::multimap, std::set and std::multiset. Erasing or modifying (if modifying is possible) an item from any of those containers does not invalidate iterators nor pointers or references to elements.
Also, if you store pointers in std::vector or other containers that don't have the property, then the stored pointer to the object still remains valid even though indices, pointers, references and iterators to the stored pointer become invalid. There is a stable_vector template in boost that stores pointers to the element and does not invalidate iterators or pointers to the element when the container is modified. The indices do of course become invalid if elements are removed and obviously it doesn't have the advantage of contiguous memory.
About your original problem:
Given your requirements, returning a set of set of iterators/pointers to the original container seems indeed appropriate. And if the iterators must remain valid when the original container is be modified later, say by adding more points or by removing points that are not referred by any partition, then the type of the original container must indeed be such as discussed on this page.
You can use a std::map with the pointers as the key and since the keys are unique, no matter whatever changes you do, the addresses will not change.
eg:
std::map<int*, list<int>> x;

How can I point to a member of a std::set in such a way that I can tell if the element has been removed?

An iterator into a std::set becomes invalidated if the item it's pointing to is erased. (It does not get invalidated if the set is modified in any other way, which is nice.) However, there is no way to detect whether an iterator has been invalidated or not.
I'm implementing an algorithm that requires me to be able to keep track of members of a std::set in such a way that I can erase them in constant time, but without risking undefined behaviour if I try to delete the same one twice. If I have two iterators pointing to the same member of a set, Bad Things will happen if I try to erase both of them.
My question is, how can I avoid this? Is there some way to implement something that behaves like an iterator into a set, but which knows when it has been invalidated?
Incidentally, I'm using std::set because this is a performance critical situation and I need the complexity guarantees that set provides. I'm happy to accept answers that suggest a different data structure, but only if it allows me to (a) access and remove the smallest element in constant time, (b) remove the pointed-to elements in constant time, and (c) insert elements in O(log(N)) time or better. C++11 is OK.
You could keep a set of shared pointers. And every time you store an iterator, pair it with a weak pointer to the element. When you want to erase the element, first check the weak pointer to see if the object still exists.

std::vector and pointer predictability

when you push_back() items into an std::vector, and retain the pointers to the objects in the vector via the back() reference -- are you guaranteed (assuming no deletions occur) that the address of the objects in the vector will remain the same?
It seems like my vector changes the pointers of the objects I use, such that if I push 10 items into it, and retain the pointers to those 10 items by remembering the back() reference after each push_back.
if your vector is to store objects, not pointers to objects, are the addresses of those objects subject to constant change upon pushing more items?
Any method that causes the vector to resize itself will invalidate all iterators, pointers, and references to the elements contained within. This can be avoided by reserving mememory, or using boost::stable_vector.
23.3.6.5/1:
Remarks: 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.
No, std::vector is not a stable container, i.e. pointers and iterators may get invalidated by resizing the vector (or, better, by the corresponding reallocation). If you want to avoid this behaviour, use boost::stable_vector or std::list or std::deque instead (I would prefer the last). Or, more easily, you can simply store your locations by indices.
For more information, consider also the answer to this question here.
It's not guaranteed. If you push_back enough items to exceed the size of the memory buffer that's the backing store of the vector, a new buffer will be created, all the contents will be copied to the new location, and the old buffer will be deleted. At that point, old pointers (as well as iterators!) will be invalid.
If you know exactly how much maximum space you will ever need, you could set the size of the vector's buffer to that size when you create it, to avoid reallocation. However, I prefer to store "references" to elements of a vector as a reference to the vector and a size_t index into the vector, instead of using pointers. It's not necessarily slower than pointers (depending on the CPU type) but, even if it is, it won't be much slower and in my opinion it's worth it for the peace of mind knowing that no matter how the vector is used in the future or reallocated, it'll still refer to the proper element.