Related, the main problem with the C++ ecosystem is that everybody carves out their own language subset, so it's not one ecosystem but many ecosystems with contradicting styles and language/stdlib subsets. This makes code reuse via libraries much harder than it should be.
[0] https://hftuniversity.com/post/the-c-standard-library-has-be...
Once you get rid of the STL, compile times get so much better. With modern c++23 features, templates actually become really convenient to write, and at the core there is a really useful and pleasant to use language.
I try to avoid c++ libraries and instead rely on c-style APIs. Usually the c++ style libraries force you into using the STL, which comes with a heavy tax on compile times, without much benefit in comfort of use.
On the contrary. You can focus exactly on the features the higher level game code needs. The C++ stdlib is (for the most part) poorly designed, usually poorly implemented, the main reason for slow build times, and its complexity explodes because it needs to consider all edge cases that most code bases don't ever trigger.
A specialized dynamic array class in a few hundred lines (at most!) and with just the required features is much more useful than the 20kloc monster that's pulled in with `#include <vector>` and which doesn't even do bounds checking in the 'idiomatic' usage.
You complain about it not being suitable for game development in one comment but then expect bounds checking in release builds? You're sitting in multiple lanes at the same time.
NIH implementations are usually grossly inferior because as it turns out, it's quite hard to get it right and those edge-cases aren't important until you start getting bitten by them when you'd rather be shipping features.
Bounds checking overhead is negligible for all but the absolutely hottest code paths (fwiw we shipped active asserts, including bounds checking asserts in all the PC games I was involved with - carefully monitoring the overhead of course).
The main reason to not use the stdlib isn't so much about squeezing out the last bit of performance, but about control of what actually happens under the hood (and also compilation times). The overall runtime cost of all those active asserts (not just the range checks, everything) was somewhere in the 2..3% range, which is fine when budgeted for upfront.
I personally am more conservative on those things. I'll pick the fastest thing that is reliable.
We can all agree it's not medical systems, but audio DSP and game dev both end up rewriting a lot of STL stuff to suit their needs, and often using a restricted subset of modern C++ features for similar reasons.
That isn't some arbitrary choice, but pretty much where everyone continually ends up when solving real-time problems using C++. Whether those be games or not.
For example, both of these return the 3rd element of a std::vector:
auto val1 = vec[3]; // no bounds checking
auto val2 = vec.at(3); // bounds checkingMost of the rest of us STL is good enough.
Not uncommon for audio companies to also write their own containers and internal STL for ex. plugins as well.
Yes, it can exhibit non-optimal performance, and in some specific cases (regex's especially), extremely poor performance, but that's not the same as being poorly designed and implemented, especially given the breadth of the thing.
The ABI Nightmare - The C++ committee has this extraordinarily weird and strict rule: never break the Application Binary Interface (ABI). If a better algorithm or memory layout is discovered, the standard library cannot adopt it because doing so would change object layouts and break existing binaries. The worst part is that this ABI is never defined, so you always HEAVILY pay for what you DON'T use.
std::regex - the Programming Language Joke of the millennium. Even an interpreted language regex engine runs faster.
std::map, std::unordered_map - outdated, badly-designed and slow crap that is beaten even by high-school coders writing map data-structures.
No bounds checking. And Undefined Behavior by Default for operators like std::vector::operator[]
std::iostream - bloated, expensive design, std::vector<bool> - another joke.
Silent Iterator Invalidations causing unpredictable memory corruption.
No deprecation strategy. There are FOUR callable wrappers. At-least, have the courage to say @DEPRECATED.
No Standard Networking.
Missing System Utilities - nothing for process management, standard cryptography, or basic command-line argument parsing, etc.
To be honest, this is just the common complaints - if you run through all the stdlib features, there are dozens of severe problems. Which all the smart people know about, but are forbidden to fix - because of ABI!
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.
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
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.
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™.
* 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.
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.
People love to rag on the standards committee. I was on X3J11, the C Language Standards Committee, in 1989 ... in fact, due to alphabetical order I was the first person on the planet to vote to approve the C language standard -- the one that first standardized register and trigraphs. Standards work is hard and everyone hates you for it.
The standard library is mostly fine to use unless you have specific needs.
The bit about libraries is nonsense, sorry.
I mean, why are they blaming the standard library for inherent properties of linked lists? Yeah, you don't want to use them without good reason. That's just called picking the right data structure for the job, not a flaw with the standard library.
Some of the other choices were tradeoffs between performance and usability. The standard maps have stable iterators, whereas third-party implementations almost never do because you can write faster implementations if you're willing to live without those guarantees. Was it the right choice in hindsight? Maybe, maybe not.
I'd personally like to see a namespaced versioned standard library but like that's ever going to happen
- Keep a small stdlib, like JavaScript (especially earlier JavaScript): everyone complains about missing features, warring communities form around jQuery promises vs. Promises/A+ vs. callbacks, supply chain attacks, left-pad/is-even dependencies, etc.
- Grow a big stdlib while keeping backward compatibility, like C++: lots of cruft left around that must never be used, sitting next to newer stuff with similar names. People complain about the bloat.
- Grow a big stdlib and then break backward compatibility, like Python 2 -> 3: everyone is sad, the ecosystem churns for years.
I admit there are probably better and worse versions of each strategy, e.g., it seems to me like JavaScript's slow-but-steady accretion of primitives over time has gone OK, and it seems like apart from Python 2 -> 3 some of the PEPs I see for deprecations and replacements are reasonable. But no language has ever hit on a strategy that everyone loves, as far as I can tell.
Edit: Also not sure what can possibly be downvoted here.
Regular expressions in C++ are an example "everybody" advises against using, but it's still there. vector<bool> will stay forever and so on.