10 May 2020
How vector works - the other vectors
“The std::vector is only one kind of vector”, I’m paraphrasing Alex Stepanov here. Let’s look at the options.
Vanilla options
Options out of the box from the standard library:
-
- Built-in array
- Use it for fixed size memory contiguous sequence of values
-
std::array- Like the built-in but with an interface more similar to other containers
-
std::vector- Container to use by default, a resizeable array
-
std::deque- Gives efficient insertion at the front as well
-
std::string- Zero terminated vector of chars
Specialized
-
std::vector<bool>- It’s a specialization of
std::vectorthat, for historical reasons, came to compactboolvalues as individual bits. That’s fine in principle if it was a entirely different container, but as such it becomes quite different from the vector in terms of behaviour, e.g. can’t get reference to a individual value (a bit)
-
- use allocator for
std::vector - That’s a low level performance optimisation, so only use with measurements that justify/prove improvements
- use allocator for
Build your own
- customize the growth factor/strategy for vector
-
- size instead of pointers
- Instead of pointers store the
endandcapacityas size values, use smaller values e.g. 32-bit on a 64-bit machine. Especially for strings: when was the last time you needed to store a string longer than 4GB? This could lead to more calculations/less space trade-offs.
-
- small size optimisations
- When the contents is just a few elements, could use space inside the
endandcapacityto store contents, saving an allocation when the size is small. A lot of strings in particular are small.
-
- don’t duplicate capacity
- With enough knowledge of how the allocator works, notice that most of the time the default allocator needs to store/know the allocated size. Use that instead of duplicating the capacity as a member variable for the vector.
-
- customize handling of value types that could throw on move
- As per the previous article
-
- how empty is a “moved from” vector
- The source of move, does it get just the
beginset tonullptror more of the vector member values are set? In particular thestd::vectorguarantees thatemptyreturnstruefor a “moved from” vector. If that is implemented by checkingbegin() == end(), then for a “moved from” vector also theendpointer has to be set tonullptr, not justbegin.
-
- build your own
deque - The default implementation of
std::dequeuses chunks. This leads to particular allocation/deallocation behaviours. You could customize the size of the chunks, or just use a circular buffer, without chunks.
- build your own