In C++, STL, we have template class
.
We know that it supports O(1)
random access, and tail modification.
My question is why we don\'
Actually, this is a realistic requirement. To my knowledge, nothing in the standard mandates that the vector cannot have a buffer before the elements (v.prefix_capacity()
), just as after (v.capacity() - v.size()
). This could guarantee the same runtime for v.push_front()
and v.push_back()
, while not having any cost for those who don't use it. Additionally, it could guarantee O(1) v.pop_front()
, albeit by invalidating iterators. Fancy writing a proposal?
In the meanwhile, you might create a template (devector
?) in terms of vector, which:
pre_capacity_
initialized to 0 and a getter pre_capacity()
pre_reserve(size_t i)
which calls both:
reserve(capacity() - pre_capacity_ + i)
pre_capacity_ += i
operator[](size_t i)
and delegates to v[i + pre_capacity()]
at(size_t i)
and delegates to v.at(i + pre_capacity())
begin()
and delegates to v.begin() + pre_capacity()
vector
Or you can just track the number of elements you've pushed to / popped from front :).
We already have something as you describe in the STL. It is named deque
.
As you wrote there IS actually some overhead. So if you need this functionality and you have no problem with the overhead, use deque
. If you do not require it, you do not want the overhead, so it is better to have something that avoids this overhead, named vector
.
And as an addition: vector
guarantees that all its elements are stored in contiguous storage locations, so you can apply pointer arithmetic. This is not the case for a circular buffer.
Implementing a de-vector introduces 2 challenges:
Both are achievable, in the following manner:
One explanation is that if we want to push/pop element in the front of a vector, we must shift each element in the array by one step and that would cost O(n)
You are absolutely right, push_front
has no way to run quickly, because, in addition to a possible reallocation, all items need to be copied by one position. This gives you an amortized performance of O(n2) for n objects, which is not something that library designers wanted to encourage.
Considering that if we implement
<vector>
with circular array
Implementing vector with circular array makes it a lot harder to implement several important guarantees that must be true for a vector. For example, vector must guarantee that if iterator a
points to an element with a lower index than iterator b
, then a < b
. When vector is linear, the comparison boils down to comparing addresses of elements to which iterators a
and b
are pointing. With a circular array implementation one would need to take the address of the vector origin into consideration, which can now be in the middle of the allocated block of memory.
Another guaranteed that would be violated is this:
When
v
is avector<T>
,T
is any type other thanbool
, andn
a number between zero and vector's size,&v[n] == &v[0] + n
identity must be true.
This cannot be implemented with a circular array.