C++ std::deque implementation: why not use circular buffer? - c++

I did some search on the implementation of deque. According to this post, deque uses vector of vectors. I know pushing at the begin and end should be both in constant time, and also random access is required. I think circular buffer meets all these requirements, and is much simpler. So why not use circular buffer?
I also found the boost circular buffer. How it compares to deque?
Edit:
Ok, so it has something to do with the Iterator Invalidation Rules.It states:
all iterators and references are invalidated, unless the inserted member is at an end (front or back) of the deque (in which case all iterators are invalidated, but references to elements are unaffected)
My understanding is to overload operator like iter++, the iterators has to own a pointer to the node map and a pointer to the chunk, so if the node map is reallocated, the iterator is invalidated. But since the data never moves, the references are still valid.

A circular buffer cannot be expanded indefinitely. Eventually, you need to allocate a new one and copy all items over.
Deque can simply allocate a new vector in front of the previous ones and add a "middle" element there. I think of it as a a list of vectors rather.

Related

Will std::deque preserve pointer validity of it's contained objects?

I'm looking for a container that preserves it's contained objects' positions in memory (it's pointers remain valid)
The container will grow and shrink constantly. Elements in the middle may be erased, but there's no insertions in the middle; all elements are pushed onto the back of the container. Iterator validity isn't important in this case, my only concern is that the pointers remain valid.
Is std::deque a safe and efficient option in this situation? I was previously using list, but it is allocating far too many times to be useful in this instance.
No. std::deque is necessarily implemented using chunks. Erasing in the middle of a chunk would at the very least invalidate the addresses of all subsequent elements in that chunk.
Note that iterator invalidation and pointer invalidation are generally closely connected. An iterator often is a pointer to the (node holding the) element, with proper iteration semantics added. Such iterators get invalidated because the pointer they contain is invalidated.
Inserting or removing elements in the middle invalidates all iterators and references.
Inserting elements at the beginning/end invalidates iterators, but does not affect references.
Removing elements at the beginning/end does not affect iterators or references, except ones pointing to the removed element, and possibly the past-the-end iterator.
http://en.cppreference.com/w/cpp/container/deque/erase
All iterators and references are invalidated, unless the erased elements are at the end or the beginning of the container, in which case only the iterators and references to the erased elements are invalidated.
http://en.cppreference.com/w/cpp/container/deque/push_back
All iterators, including the past-the-end iterator, are invalidated. No references are invalidated.
(other methods that operate on front or back elements have similar notes).
NO ! As opposed to std::vector, the elements of a deque are not stored contiguously: typical implementations use a sequence of individually allocated fixed-size arrays.
The storage of a deque is automatically expanded and contracted as needed. Expansion of a deque is cheaper than the expansion of a std::vector because it does not involve copying of the existing elements to a new memory location

insert in C++ vector

I want to insert a bit from a bitset in the beginning of a vector. I am having a hard time understanding how to do that. Here is how I think I can do it:
keyRej.insert(x, inpSeq[0]);
I don't know what to put in the place of x?
I don't know what to put in the place of x?
An iterator to the position you want to insert in:
keyRej.insert(keyRej.begin(), inpSeq[0]);
Semantically, the inserted element goes before the iterator passed as first argument. But this will result in all elements of the vector having to be moved across one position, and may also incur a re-allocation of the vector's internal data storage block. It also means that all iterators or references to the vector's elements are invalidated.
See this reference for std::vector::insert for more information.
Note that there are containers, such as std::deque, for which appending elements to the front is cheap, and reference (but not iterator) validity is maintained.
x is an iterator according to the documentation you probably read here, the new object is insert just before it.
keyRej.insert(keyRej.begin(), inpSeq[0]);

invalid iterator with random access iterators with deque

I am reading effective STL by Scott Meyers. Here in item 1 author is mentioning about how to choose among various containers and below is text snippet which I am having difficulty in understanding.
Would it be helpful to have a sequence container with random access
iterators where pointers and references to the data are not
invalidated as long as nothing is erased and insertions take place
only at the ends of the container? This is a very special case, but if
it’s your case, deque is the container of your dreams.
(Interestingly,deque’s iterators may be invalidated when insertions
are made only at the ends of the container. deque is the only standard
STL container whose iterators may be invalidated without also
invalidating its pointers and references.)
My questions on above text
What does author mean by pointers and references in above context and how is it different from iterators?
How deque's iterators may be invalidated when insertion made only at end and still we have valid pointers and references?
Request above two questions to be answered with simple example.
Thanks for your time and help.
For the first part, what's meant is this:
deque<int> foo(10, 1); // a deque with ten elements with value of 1
int& bar = foo.front(); // reference
int* baz = &foo.front(); // pointer
deque<int>::iterator buz = foo.begin(); // iterator
deque.push_front(0);
// At this point bar and baz are still valid, but buz may have been invalidated
For the second part it's been covered in the detail here:
Why does push_back or push_front invalidate a deque's iterators?
An iterator is often used to "cycle through" the elements of a standard-library container, much like you would do with an array index, e.g. in a for loop.
Iterators can be invalid for many reasons. One common case where this happens is when you use a for loop such as the following:
std::deque<int> c;
for(std::deque<int>::iterator i = c.begin(); i != c.end(); ++i) {
// do some stuff to the deque's elements here
}
At the end of the above loop, the iterator i will point to an "element" one block after the last real element in the deque. If you tried to do something like
*i = 88;
right after the end of the above for loop that would be a problem because the container does not "own" the memory i "points" to.
But what Meyers is likely talking about is that the Standard leaves much of the implementation of a deque open to the designer. Deques are usually implemented as linked-lists of blocks of memory holding several elements, so unlike vectors there is no guarantee that elements will be next to each other in memory. Furthermore, iterators necessarily contain information about these "blocks" so that they can traverse them smoothly (i.e. iterators are not simply pointers).
For example, if I push_back() a new element, but there is no more room in the "last" chunk of memory, then deque will need to allocate a new block of memory for the new element (and future elements added to the end). Since an iterator I was using previously might not "know" about this new chunk of memory, it could be invalid.
References and actual pointers, on the other hand, would be used in this context to refer/point to individual objects in the container. If I write
int& j = *c.begin();
then j is a reference to the first element of c. If I then do
c.push_front(74);
j still references that previous first element, even though it is no longer at the front of the deque.
However, if you insert something in the middle of the deque, then chances are you are effectively splitting one of those contiguous chunks of memory and trying to squeeze your new element in there. To make room, elements on one side or the other must be shuffled around in memory (and possibly new memory needs to be allocated). This would by necessity invalidate pointers/references to elements on that "side" of the insertion. Since it is up to the implementer how exactly room is made for an inserted element, all bets are off with respect to any pointer/reference, no matter where it is with respect to the insertion.

Deque erase from middle, is pointer lost?

i erase elements from middle of my deque frequently. memory address of elements in deque are used in some other places. does erasing middle element from deque invalided all pointers to deque like it does with vectors? should i switch to list?(i iterate whole deque anyways)
i tried to read deque implementation but it is complex, i don't understand if it acts as a list or vector.
I'm using visualc++ std implementation of deque.
Yes. An insertion in the middle invalidates all iterators and references to elements (and thus pointers to elements). An insertion at either end invalidates all iterators, but not references or pointers. And you don't read the implementation to find out such things; you read the documentation. (The implementation may actually allow operations which aren't officially supported. Until the next bug fix.)
does erasing middle element from deque invalided all pointers to deque like it does with vectors?
In case of a deque all iterators and references to the deque are invalidated, unless the erased members are at an end (front or back) of the deque.
For a vector all iterators and references before the point of insertion are unaffected, unless the new container size is greater than the previous capacity.

C++: how to track a pointer to a STL list element?

I would like to track a pointer to a list element for the next read access. This pointer would be advanced every time the list is read. Would it be bad practice to cache an iterator to the list as a member variable, assuming I take proper precautions when deleting list elements?
An iterator to a list element remains valid until the element it refers to is removed. This is guaranteed by the standard.
There is no problem with caching an iterator as long as you make sure you don't remove elements from the list or refresh the cached iterator when you do.
Iterators are meant to be used. They aren't just for looping over each element in a for-loop. All you have to take into account is the rules for iterator invalidation.
std::vector iterators can be invalidated by any operation that inserts elements into the list. The iterators for all elements beyond the point of insertion are invalidated, and all iterators are invalidated if the insertion operation causes an increase in capacity. An operation that removes an element from the vector will invalidate any iterator after the point of removal.
std::deque iterators are invalidated by any operation that adds or removes elements from anywhere in the deque. So it's probably not a good idea to keep these around very long.
std::list, std::set, and std::map iterators are only invalidated by the specific removal of the particular element that the iterator refers to. These are the longest-lived of the iterator types.
As long as you keep these rules in mind, feel free to store these iterators all you want. It certainly isn't bad form to store std::list iterators, as long as you can be sure that that particular element isn't going anywhere.
The only way you're going to be able to properly advance though an STL std::list<T> in a platform and compiler independent way is through the use of a std::list<T>::iterator or std::list<T>::const_iterator, so that's really your only option unless you're planning on implementing your own linked-list. Per the standard, as others here have posted, an iterator to a std::list element will remain valid until that element is removed from the list.