Various 'features' of C++ that show the hacky / inconsistent way in which the language was constructed. This is a work in progress, and currently contains some of the reasons I can remember why I've given up on C++. If you want to contribute, leave your favourite "hack" in the comments.
-
(in)Visibility: C++ allows changing the access modifier of a virtual function in the derived class. Not only does C++ have no notion of interfaces, it actually allows subclasses to hide methods declared public in the superclass.
-
Operator over-overloading: One of the increment operators takes a dummy int parameter in order to allow overloading. Can you tell which without googling? (hint: its postfix).
-
Exception unspecifiers: C++ has two types of exception specifiers: throw() and nothrow. The first is deprecated (because 'we screwed up, sorry, let's forget about this terrible mess'). The second one guarantees it's contract by terminating the application when violated. That's because functions declared nothrow can still call legacy functions that don't declare anything (backwards compatibility and such), so the compiler can't validate your claim, choosing to explode in your face at runtime.
-
Dependency hell: Without adding another layer of indirection via the PIMPL idiom, changes to the internal structure of the class propagate to all it's users, making compilation painfully / unecessary slow. Granted, the C++ object model requires this when the modified class is used as a value field inside another class, but C++ has no way of distinguishing between use cases. This is what you get for using a text preprocessor for handling dependencies.
-
Binary incompatibility: Linkers are not standardised, so binary compatibility must exist between compilation units when linking. Not only are compilers incompatible, the same compiler used with different flags can generate incompatible obj files. Write once, compile everywhere.
-
Multithreading: C++ did not have any standard multithreading implementation until C++ 11.
-
RValue References: C++ has value semantics, which means it generates a copy constructor to deep copy value fields (convenient huh?). While the standard committe thought it was pretty cool, they eventually realised how many times objects were unecessary or accidentally copied behind the scenes (return values from functions, array operations, etc). So they invented 'move semantics', 'move constructors', and a new synthax for overloading on ephemeral 'rvalue references': &&.
-
Name mangling: The C++ compilator was originally a preprocessor for C (CFront), and still maintains binary compatibility with C. C doesn't allow function overloading and doesn't have namespaces, so any function can be uniquely identified through its name (to avoid collisions, most libraries use a prefix when naming functions, such as gl*). Since C++ has function overloading, namespaces and classes, it allows multiple functions / methods to share the same name. The C++ compiler solves this problem by generating a unique identifier for each function. This is called name mangling, and uses the function's name, parameters, return type, namespace and class to create a unique name. The result is a horrible abomination that makes linker errors a pain to descipher. It's also non-standard, so every compiler has it's own name mangling scheme, making them incompatibile with each other. - https://isocpp.org/wiki/faq/mixing-c-and-cpp
-
Inheritances: Since there are multiple access modifiers (public, protected, private), why not multiple inheritance(s)? There's even a virtual inheritance! While their meaning is beyond the scope of this rant, suffice it to say there are aspects that make them non trivial to understand / use. Scott Meyers wrote in his famous Effective C++ book: "protected inheritance is something whose meaning eludes me to this day". And he's a C++ guru.
I personally don't think a lacking multithreading implementation is a reason to disregard a language. How did that stop python from becoming large? Also, there are many well working multithread libs out there.