I'll be as brief as I can:
- Implicit type conversions. How the following example compiles OK
without a warning is beyond me:
#include <iostream>
void foo(const std::string &) {}
int main()
{
foo(false);
}
- The "type name or object name?" issue is one of the little things
that can make parsing C++ a nightmare. You can get different parse
trees based on the output of sizeof, for instance. And sizeof is
dependent on the platform and implementation of inheritance, virtual
functions, etc.
- Current programming idioms (in C++, that is) and its lack of
reflection force us to use static binding to implement polymorphism.
So, for a common and seemingly simple application of metaprogramming,
such as printing objects at runtime, you'll get N layers of syntax and
meticulously placed overloads of operator<< in carefully #include'd
header files, and even in that case some compilers will generate
erroneous code without as much as a warning (that is, if they got past
the implicit type conversion hell).
- The conscious decision of not adding high-level types has led to,
most significantly, lack of easy-to-use containers. It has its
upsides, I agree. However, when you get around to use (and not
implement) ADTs, debugging can be a bitch. And when it comes to
implementation, at the heart of every high-level container you'll find
templates, relatively sophisticated ones too in case you're dealing
with the standard library and even more so with the Swiss army knife
that's called Boost. Everyone knows in what ways templates suck, I
won't list them.
- Duplicate facilities. For everything from printing to initializing
an object to using an array. They don't interoperate very well (check
out some code dealing with char* and std::string if you feel like
getting an aneurysm), and for that reason the C++ crowd shuns the use
of C syntax and features in C++ programs, although they still claim
compatibility with C is a good thing. I always chuckle when I see that
written. Heh.
- Manual memory management. A good thing. I support this 100%. It has
its place in the language and in today's systems. But it's
incompatible with templates and operator overloading. Can't have it
all (maybe in another language

.
- Inlining, although not as much related to metaprogramming as the
ones above. For this part, I'm quoting: "People define large functions
inline for two reasons. Some of them "care" (emotionally) about
performance, but never actually measure it, and someone told them that
inlining speeds things up, and forgot to tell them how it can slow
them down." Like when inline functions are implicitly called or
generated. Sometimes you'll have a huge class that needs to be
accessible globally. A singleton, I hear you saying! Nah, perhaps
other facilities of your program need to notify it, or modify it, and
others need to see the changes. Anyway that huge class will at least
get created and destroyed once. But that class will probably have no
constructor, no destructor, no operator= either - the compiler will
generate code for those anyway. And that may not be a problem until
you crash nearby and get a pile of assembly (which you haven't
written, it's not even your compiled code!) without any diagnostics
whatsoever.
I mean, those are just a few. I've seen literally thousands of
complaints about the language constructs, and sadly most came from
people who were way more knowledgeable than me on the issues at hand.
It's not that C++ is bad, it's very useful and rewards you when you're
not overzealous, but it's unsuitable for certain types of problems,
for which it's (sadly, again) used for very often. That's one of the
disadvantages of people calling it Object Oriented, without actually
being such in a satisfying degree, I guess. At least BS is less stuck
up than most people, when it comes to it.
Bah, sorry for the wall of text. I guess I couldn't help it.
