Given an STL container (you may also take boost::unordered_map and boost::multi_index_container into account) that is non-contiguous, is it guaranteed that the memory addresses of the elements inside the container never changes if no element is removed, (but new ones can be added)?
e.g.
class ABC { };
//
//...
//
std::list<ABC> abclist;
ABC abc;
abclist.insert(abc);
ABC * abc_ptr = &(*abclist.begin());
In other word will abc_ptr be pointed to abc throughout the execution, if I do not remove abc from abc_list.
I am asking this because I am going to wrap the class ABC in C++/Cli, so I need pointers to the ABC instances in the wrapper class. ABC is a simple class and I want the container to handle the memory. If the answer is no then I will use std::list<ABC*>.
std::list, std::set, and std::map guarantee that the iterators (including simple pointers) will not be invalidated when a new element is added or even removed.
As Armen mentioned std::list, std::set, and std::map are guaranteed to only invalidate the removed iterator. In the case of boost::unodered_map, the modifiers may indeed invalidate iterators.
http://www.boost.org/doc/libs/1_38_0/doc/html/boost/unordered_map.html
The C++ Standard places stringent rules on the validity of references / iterators. For each container, each method documents which elements may be moved (invalidating references and iterators).
The Node Based Containers: list, map, set, multimap and multiset guarantee that references and iterators to elements will remain valid as long as the element is not removed from the container.
Your use case is therefore one of the corner cases where using a list for storage is good, because of the invalidation guarantees that list offer.
I think it's better to use std::list <shared_ptr <ABC> > instead of passing a pointer.
It's good practice to delegate memory management (see scott meyers effective c++)
This has mulitple advantages:
you can share them and pass them without the headache of freeing them
garbage collection of your pointers
you don't pass a pointer in the first place
Related
Why do we have to write v.erase(v.begin(), v.begin()+3) ?
Why isn't it defined as erase(int, int) so you can write v.erase(0,2) and the implementation takes care of the begin()s?
The interface container.erase(iterator, iterator) is more general and works for containers that don't have indexing, like std::list. This is an advantage if you write templates and don't really know exactly which container the code is to work on.
The original design aimed at being as general as possible, and iterators are more general than indexes. The designers could have added extra index-based overloads for vector, but decided not to.
In STL, iterators are the only entity that provides general access to STL containers.
The array data structure can be accessed via pointers and indexes. iterators are generalization of these indexes/pointers.
Linked list can be accessed with moving pointers (a la ptr = ptr->next). iterators are generalization to these.
Trees and HashTables need special iterator class which encapsulates the logic of iterating these data structures.
As you can see, iterators are the general type which allows you to do common operations (such as iteration, deletion etc.) on data structures, regardless their underlying implementation.
This way, you can refactor your code to use std::list and container.erase(it0, it1) still works without modifying the code.
Is there a way to delete an element from an STL container (be it list, vector, ...) only via an iterator pointing to the element to be deleted, but without providing the container object it resides in (i.e. without directly using the container memberfunction container<T>::iterator container<T>.erase(container<T>::iterator)?
(Follow-up to this question)
No, this is not possible.
Imagine what would have to happen in order for the standard to provide a way to do this. std::vector<T>::iterator could not be a simple T*. Instead it would have to contain enough information for the library to be able to "find" the vector to which it belongs, such as a pointer to the vector itself. Thus, if the standard imposed a requirement to make it possible to delete an element given only an iterator, it would force the standard library to add overhead that would slow down all users of the container.
I have been overthinking (some may say underthinking, let's see what happens) the const-ness of STL containers and their elements.
I have been looking for a discussion of this, but the results have been surprisingly sparse. So I'm not necessarily looking for a definite answer here, I'd be just as happy with a discussion that gets the gears in my head moving again.
Let's say I have a class that keeps std::strings in a std::vector. My class is a dictionary that reads words from a dictionary file. They will never be changed. So it seems prudent to declare it as
std::vector<const std::string> m_myStrings;
However, I've read scattered comments that you shouldn't use const elements in a std::vector, since the elements need to be assignable.
Question:
Are there cases when const elements are used in std::vector (excluding hacks etc)?
Are const elements used in other containers? If so, which ones, and when?
I'm primarily talking about value types as elements here, not pointers.
My class is a dictionary that reads words from a dictionary file. They will never be changed.
Encapsulation can help here.
Have your class keep a vector<string>, but make it private.
Then add an accessor to your class that returns a const vector<string> &, and make the callers go through that.
The callers cannot change the vector, and operator [] on the vector will hand them const string &, which is exactly what you want.
No, for the reason you state.
In the context of std::vector, I don't think it makes sense to use a const qualifier with its template parameter because a std::vector is dynamic by nature and may be required to "move" in memory in order to "resize" itself.
In the C++03 standard, std::vector is guaranteed stored in contiguous memory. This almost requires that std::vector be implemented with some form of an array. But how can we create a dynamic size-changing array? We cannot simply just "append" memory to the end of it--that would either require an additional node (and a linked list) or actually physically putting our additional entries at the end of the array, which would be either out-of-bounds or require us to just reserve more memory in the first place.
Thus, I would assume that std::vector would need to allocate an additional array, copy or move its members over to the end array, and then delete the old one.
It is not guaranteed that a move or copy assignment for every template-able object for a std::vector would not change the underlying object being moved or copied--it is considered good form to do add the const qualifier, but it is not required. Therefore, we cannot allow a std::vector<const T>.
Related: How is C++ std::vector implemented?
consider using
std::vector<std::shared_ptr<const std::string>>
instead?
From a C background I find myself falling back into C habits where there is generally a better way. In this case I can't think of a way to do this without pointers.
I would like
struct foo {
int i;
int j;
};
mymap['a'] = foo
mymap['b'] = bar
As long as only one key references a value mymap.find will return a reference so I can modify the value, but if I do this:
mymap['c'] = mymap.find('a') // problematic because foo is copied right?
The goal is to be able to find 'a' or 'c' modify foo and then the next find of 'a' or 'c' will show the updated result.
No, you will need to use pointers for this. Each entry in the map maintains a copy of the value assigned, which means that you cannot have two keys referring to the same element. Now if you store pointers to the element, then two keys will refer to two separate pointers that will refer to the exact same in memory element.
For some implementation details, std::map is implemented as a balanced tree where in each node contains a std::pair<const Key,Value> object (and extra information for the tree structure). When you do m[ key ] the node containing the key is looked up or a new node is created in the tree and the reference to the Value subobject of the pair is returned.
I would use std::shared_ptr here. You have an example of shared ownership, and shared_ptr is made for that. While pointers tend to be overused, it is nothing wrong with using them when necessary.
Boost.Intrusive
Boost.Intrusive is a library presenting some intrusive containers to
the world of C++. Intrusive containers are special containers that
offer better performance and exception safety guarantees than
non-intrusive containers (like STL containers).
The performance benefits of intrusive containers makes them ideal as a
building block to efficiently construct complex containers like
multi-index containers or to design high performance code like memory
allocation algorithms.
While intrusive containers were and are widely used in C, they became
more and more forgotten in C++ due to the presence of the standard
containers which don't support intrusive techniques.Boost.Intrusive
not only reintroduces this technique to C++, but also encapsulates the
implementation in STL-like interfaces. Hence anyone familiar with
standard containers can easily use Boost.Intrusive.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
I have a class that spawns an arbitrary number of worker object that compute their results into a std::vector. I'm going to remove some of the worker objects at certain points but I'd like to keep their results in a certain ordering only known to the class that spawned them. Thus I'm providing the vectors for the output in the class A.
I have (IMO) three options: I could either have pointers to the vectors, references or iterators as members. While the iterator option has certain draw backs (The iterator could be incremented.) I'm unsure if pointers or references are clearer.
I feel references are better because they can't be NULL and a cruncher would require the presence of a vector.
What I'm most unsure about is the validity of the references. Will they be invalidated by some operations on the std::list< std::vector<int> >? Are those operations the same as invalidating the iterators of std::list? Is there another approach I don't see right now? Also the coupling to a container doesn't feel right: I force a specific container to the Cruncher class.
Code provided for clarity:
#include <list>
#include <vector>
#include <boost/ptr_container/ptr_list.hpp>
class Cruncher {
std::vector<int>* numPointer;
std::vector<int>& numRef;
std::list< std::vector<int> >::iterator numIterator;
public:
Cruncher(std::vector<int>*);
Cruncher(std::vector<int>&);
Cruncher(std::list< std::vector<int> >::iterator);
};
class A {
std::list< std::vector<int> > container;
boost::ptr_list< std::vector<int> > container2;
std::vector<Cruncher> cruncherList;
};
If an iterator is invalidated, it would also invalidate a pointer/reference that the iterator was converted into. If you have this:
std::vector<T>::iterator it = ...;
T *p = &(*it);
T &r = *p;
if the iterator is invalidated (for example a call to push_back can invalidate all existing vector iterators), the pointer and the reference will also be invalidated.
From the standard 23.2.4.2/5 (vector capacity):
Notes: Reallocation invalidates all the references, pointers, and iterators referring to the elements in the sequence.
The same general principal holds for std::list. If an iterator is invalidated, the pointers and references the iterator is converted into are also invalidated.
The difference between std::list and std::vector is what causes iterator invalidation. A std::list iterator is valid as long as you don't remove the element it is referring to. So where as std::vector<>::push_back can invalidate an iterator, std::list<>::push_back cannot.
If the parent's vector's contents is re-allocated after spawning your worker threads, then their pointers, references, iterators, or whatever are almost certainly invalid. A list MAY be different (given how they're allocated) but I don't know, and may even be platform-dependent.
Basically, if you have multiple worker threads, it's probably safest to actually have a method on the parent class to dump the results back into as long as the copy isn't that taxing. Sure it's not as good as allocating directly into the parent, but then you need to ensure the container you're dumping into doesn't get "lost" on re-allocation.
If the list you're using is guaranteed not to re-allocate its "other" space when members are added (or deleted), then that would achieve what you're looking for, but the vector is definitely unsafe. But either way, the way in which you access it (pointer, reference, or iterator) probably doesn't matter that much as long as your "root container" isn't going to move around its contents.
Edit:
As mentioned in the comments below, here's a block about the list from SGI's website (emphasis mine) :
Lists have the important property that
insertion and splicing do not
invalidate iterators to list elements,
and that even removal invalidates only
the iterators that point to the
elements that are removed. The
ordering of iterators may be changed
(that is, list::iterator might have
a different predecessor or successor
after a list operation than it did
before), but the iterators themselves
will not be invalidated or made to
point to different elements unless
that invalidation or mutation is
explicit.
So this basically says "use a list as your master store" and then each worker can dump into its own, and knows it won't get invalidated when another worker is completely done and their vector is deleted from the list.
In the current version of C++ (i.e. no move constructors) then pointers into items embedded in a std::list will be invalidated along with the list iterators.
If however you used a std::list*>, then the vector* could move around but the vector would not, so your pointer into the vector would remain valid.
With the addition of move constructors in C++0x the vector content is likely to stay put unless the vector itself is resized, but any such assumption would be inherently non-portable.
I like the pointer parameter. It is a matter of style. I prefer this parameter type style:
Const reference: Large object is being passed for reading. Reference avoids wasteful copying. Looks just like pass-by-value at the point of call.
Pointer: Object is being passed for reading and writing. The call will have an "&" to get the pointer, so the writing is made obvious during code review.
Non-const reference: banned, because code review can't tell which parameters might get changed as a side effect.
As you say, an iterator creates a pointless dependency on the parent container type. (std::list is implemented as a double-linked list, so only deleting its entry invalidates a vector. So it would work.)