3 ms·
Why do we make things so complicated? Because C++ is a low-level abomination pretending to be a high-level language. Let's start. - Allocators in the STL are
by zvrba 15y ago
Why do we make things so complicated? Because C++ is a low-level abomination pretending to be a high-level language. Let's start.
- Allocators in the STL are a joke, so you can't use them for implementing different memory models. So you end up implementing, e.g., linked lists from scratch.
- You can't "steal" underlying storage from a vector, which means you can't use it as a simple memory pool. So you end up implementing a vector from scratch.
- size() of containers returns an unsigned type, which is a perpetual source of code ugliness and cursing in may day-to-day programming. (The most trivial example: make a backward loop through a vector. No, don't tell me to use iterators -- index has also semantical meaning in my use-cases.)
- There is static_cast, reinterpret_cast, dynamic_cast, const_cast, but no cast which would actually check that what you're trying to do makes sense. (C and C++ standards stipulate that casting from A to B is well-defined A is a "prefix" of B. This could be extended to check for layout compatibility, so it would be legal to cast a linearly triply-derived struct with 3 floats to a float array with 3 elements. Technically, it's UB, but "it works in practice", and that "it works" could have been formalized. The compiler would be allowed to reject non-working cases.)
- Writing flexible, generic (template) code is all nice in theory, but, in practice, it goes against the idea of separate compilation, with corresponding increased compilation times.
- Debugging heavily templated code is a nightmare.
- Writing templated code is arduous. Templates being turing-complete, we can never expect as good IDE support as for Java and C#. (I toyed with some simple Java programs in NetBeans and I almost cried when I had to back to my day-job of writing C++. IntelliSense of Visual Studio, like "state of art" IDE for C++, is CRAP compared to what you get from NetBeans/Eclipse for Java.) I like more and more the idea of using a "restricted" language augmented with a powerful IDE.
- The idea of constructors establishing class invariants is all well and nice until you have to deserialize an object graph with cycles. (For this you must temporarily have an invalid object that is to be filled in later.) Constructors/destructors make sense if you actually have to manage resources.
- etc, etc.
In short, C++ has a history of incorporating half-baked features into the language and/or standard library. Things get complicated because it IS complicated to get a half-baked feature to do what you actually need to accomplish.
What half-baked features? I've already mentioned allocators and casting.
Then there's RTTI, whose implementation is complex, but I don't know whether any serious project uses it for something more than dynamic_cast. (In reality, you would like to have full reflection, but you have to implement it yourself. Yes, it's complicated.)
When you try to implement reflection yourself, you will, almost invariably, use offsetof at some point, which, technically, yields UB in most cases, but also it WORKS in the most common cases (no MI, no virtual inheritance).
There is auto, there is declspec, but there is no complete type-deduction, so you often have to spell out things twice to the compiler, even though it already has all information it needs.
There is no structural polymorphism [this is related to lacking casts] -- you're supposed to write template code with corresponding bloat of object code and other, above-mentioned downsides of templates.
iostreams are barely useful: when opening a stream fails, there is no standard-defined way of finding out WHY it failed (non-existent file, permission denied, etc.. This is a KEY feature for any program that interacts with a human user.) In C, IO functions set the errno variable, but not C++ streams. If you're lucky, errno will be set by the underlying system call and won't be changed further, but you can't count on that. Yet another fail (i.e., half-baked feature).
Another iostreams misfeature: overloaded operators << and >> : the program code leads you to believe that things execute sequentially, where, in fact, the code is converted to function calls with unspecified evaluation order of arguments. So if f() has side-effects, you don't know what cout << f() << f() will output.
==
Maybe the new C++ standard fixes some of the above issues, but I'm so disappointed by the overviews of what's new, that I won't even bother checking. Many things are just cosmetic, some are outright stupid (e.g., user-defined literals), but fundamental problems of piled-up, half-baked features remain.