Some of the complexity of C++ comes from it doing hard things, some is in part a consequence of heroic efforts at backwards compatibility. There are areas of the language that most people stay away from or use in very constrained ways like multiple inheritance that are unlikely to be deprecated for backwards compatibility reasons but in my many years of professional C++ development multiple inheritance has never caused practical difficulties for me precisely because everybody stays away from it except for pure abstract interfaces.
> C++ has a standard which contains extremely precise descriptions of how things work which is more than can be said for many languages.
(0) Most programming languages don't set the bar very high.
(1) You missed the “short” part. The C++ standard is already pretty long, and it isn't even written in a form that makes it possible to prove things about C++ programs by consulting the standard.
> How many other languages are there that have three major compiler and standard library implementations with almost no common code yet which all manage to be largely compatible (able to compile the same code and agree on the meaning)?
I can think of at least four: Standard ML, Common Lisp, C, Java.
> Some of the complexity of C++ comes from it doing hard things,
In unintelligent ways. For example:
(0) Macro, pardon my French, template expansion as a generic programming tool is super dumb. Better alternatives were known in the 70's.
(1) C++ classes are very poor abstract data types: You need arcane language design hacks like `friend` to abstract over two or more types at once.
(2) C++ classes are also very poor object constructors: The language has no type for “all objects that have methods foo, bar, qux” (à la OCaml or Go).
> some is in part a consequence of heroic efforts at backwards compatibility.
Most of it.
Instead, they add new features that lets new code be written in new ways without requiring you to toss out your old code. Your old C code is full of mallocs and frees. Your old code still works when you partially update it using the newest features. But, once C++ added new and delete, you rarely ever needed to type malloc or free any more unless you were overloading new and delete. Your old C++ code is full of news and deletes, but new language features added in C++11 made unique_ptr and shared_ptr possible. And, now you rarely need to type new or delete unless you are making your own unique_ptr/shared_ptr variant.
I am by no means arguing that C++ committees are making any mistakes.