1. Stepanov's generic programming is a good idea. Every language you've seen with "generics" that's his idea, to the extent "The STL" is generic programming, everybody agreed it's a good idea.
2. But the STL is very old now, so while the idea is good, this is one of the oldest (Stepanov had tried this in other languages before C++) implementations and so other implementations are often better, because they've learned from experience
3. As well as pretty good generic algorithms, the STL also provides a lot of container types (what Rust would call collection types) and these vary not between "excellent" and "mediocre" but between "mediocre" and "inexplicably terrifying". The most charitable explanation is that they're just intended for teaching. If you teach DS&A to a Computer Science class you want the Extrusive Doubly Linked List to teach in class. If you write software you almost certainly never need this type, but it's front an centre in the C++ STL.
There's a single "I guess I would use this" container type, std::vector. It has an insane special case for bool, because WG21 are idiots, but it's otherwise a good enough growable array type and it's not worth building your own instead given the constraints.
Everything else is silly, or bad, or both. std::unordered_map feels like a hash table I made in class in the mid 1990s, but it's actually the provided standard hash table container in C++ 11 onwards. std::list is just that extrusive linked list for some insane reason. The Microsoft standard library maintainer STL could not offer me any justification for what std::deque is actually supposed to be for.
About that one... I would claim that in a majority of cases where an std::vector is used, what the author really wanted was a similar type, but whose size and capacity are fixed on construction and never change. The standard C++ library does not offer such a type - so people use vector because it's handy.
Agree with your takes on most of the containers. I also dislike how optionals are never used with containers as they were standardized later (and even then, problematically w.r.t. references). Thus, for example, if I lookup an object in a map of T's, the result should IMNSHO be an optional reference to a T.
* APIs/function signatures explain more clearly what are the intended uses of the structure that's passed.
* More potential for compiler optimization
* Some potential for having these on the stack (if the compiler deduces the size already at compile-time)
* More convenient for static analysis
* No plethora of confusing constructors (including the infernal two-element ctors which can be misinterpreted super-easily)
etc.
But I don't think that makes such a container worth adding to the STL. So far, it hasn't even been worth writing in our own code. But that's the reason I've thought about writing it.
https://uecker.codeberg.page/2025-07-20.html
I probably will still add a version with capacity, because people may insist on it, but personally I like the one without much more so far.
There is std::array for that. Also, for a type with fixed capacity but variable (up to that capacity) size, we're getting std::inplace_vector soon™.
If you want to get f cy you can also give it a custam allocator and throw an exception if you do this by mistake
But that's the point. You _can_ push_back, or resize, or reserve, etc. etc. My claim is that the more common use case is the one where this _cannot_ happen.
> If you want to get f cy you can also give it a custam allocator
And then nobody will want to use it, because that's not interchangeable with containers vector with the default allocator, and also difficult to understand. I mean, look at PMR allocators, which are not even that custom. They see very little use I'm afraid.
Also, do I understand correctly that you want both the capacity and the size fixed, i.e., the container's elements have to be pre-constructed and supplied to the constructor via an initializer list?
Case in point: list::sort. You don't want to try running quicksort on a linked list. Or remove_if: great we've abstracted the difficult task of removing things without erasing them, except we can't do it on maps. (C++20 seems to add an erase_if, apparently admitting that the two-step remove/erase is silly).
Then there's the fact that C++ iterators are basically pointers into the data structure, where for vectors (your common case) you'd do much better with index/container pairs, both for stability and bounds checking.
STD::sort only works on a random access iterator, it won’t even compile if you try it on a list
Unlike in std::unordered_map you are getting some value here, the RB tree is able to achieve meaningful efficiency gains from its use of pointers. The trouble is that your computer isn't a PDP-11, a hybrid which does fewer dereferences but has more locality will trade well here on a real computer built this century.
So, I'm torn, it's not awful but it's a problem that std::map is basically obliged to be a Red Black Tree even though that's not a good fit for today's machines.
I see that problem much more often than crashes due to unchecked map lookups in production, which are very rare for me. Less than once a year.