
The Dark Side of C++ (2007) - KonradKlause
http://www.fefe.de/c++/c%2b%2b-talk.pdf
======
dkarl
Again the operator overloading thing.

Yes, it's true. && might do something different than you might expect if
you're a C programmer. If you're a C++ programmer, then you know somebody
might have overloaded the && operator. Yes, they could do it in some weird and
unexpected way. Just like somebody can declare a weird and unexpected macro in
C.

Yes, people who define C++ classes you use can screw you with dangerous or
incorrect behavior, like throwing exceptions from destructors. Just like
people who write C libraries can screw you with dangerous or incorrect
behavior.

auto_ptr is not useless. At least, I assume it's good for something. Who
knows? If not, who cares? boost::shared_ptr is "poor man's garbage
collection," not auto_ptr. If you cite the documented behavior of a type as a
huge and unwelcome surprise, then gosh, I know exactly how to avoid surprises
like that: read the freaking documentation _before_ you use the type.

Oh, and again with the "I can't tell what this code does." Here's a crazy
scenario in C: call a function. Do you know what the code does? No, you don't.
How awful! Unless you look at the function you just called, or maybe just
glance at its documentation.

Half of these criticisms of C++ boil down to, "It's okay for functions to have
unknown behavior that I have to learn about before using them, because I'm
used to that, but it's not okay for types to have any kind of unknown
behavior, because I'm used to C, where that is not the case."

~~~
coliveira
> It's okay for functions to have unknown behavior that I have to learn about
> before using them, because I'm used to that, but it's not okay for types to
> have any kind of unknown behavior, because I'm used to C,

It is not an issue of behavior but of understanding.

In C++, a large number of things can be happening in a single line: an
operator being called, a type conversion, a template instantiation, an
overriden function in one of a dozen of classes, a constructor called in some
part of the hierarchy, in some namespace... who knows. You have to be smart
enough to figure out what is going on in each of these cases. As you code base
grows, this makes it increasingly hard to understand what is going on.

~~~
mattgreenrocks
By that logic, you shouldn't use any dynamic language, because someone could
monkey-patch every function and you'd have no guarantee what exactly was being
called.

Also, you forgot macros in your list of gripes, as they can stomp on literally
ANYTHING indiscriminately, despite being declared far away from the actual
invocation point.

~~~
nightski
+1 on dynamic languages, not a fan for this and many other reasons

Haskell seems to go a long way towards solving this problem in my humble
opinion. Meaning it can have very dense code but as long as you follow the
types it is often fairly straightforward to figure out what a piece of code
does.

The worst part about all the things that can happen implicitly in C++ is that
they can all have side effects! Oh fun. This is not just a pitfall of C++
either, Java/C# can have the same issue - one statement can trigger a whole
plethora of side effects making a large brownfield development project one
hell of a nightmare.

~~~
danieldk
_Haskell seems to go a long way towards solving this problem in my humble
opinion. Meaning it can have very dense code but as long as you follow the
types it is often fairly straightforward to figure out what a piece of code
does._

As a Haskell fan, I have to say that there is a bag of hurt here as well:
performance in the lazy evaluation regime. It can be very hard to
predict/understand when thunks are evaluated and the effect it has on heap
use.

I am still hoping for a strict, pure language with monads to go mainstream
enough to use it.

 _The next Haskell will be strict_ \- Simon Peyton Jones

Source: <http://www.cs.nott.ac.uk/~gmh/appsem-slides/peytonjones.ppt>

------
rgarcia
I have to disagree with some of the "warts":

 _operator[] adds a member to a map if it does not already exist_

The main theme of this article seems to be "C++ gives you too much
flexibility," but this is a case where C++ is enforcing a default behavior and
_limiting_ your flexibility. In Python, for example, understanding code that
queries for a key in a dictionary requires you to know some context about
whether that dictionary has a default value for missing keys. For all the
quibbling the OP does about operator overloading, I'd expect him to be
grateful for the predictable nature of map's operator[].

 _local static initialization is not thread safe_

This is just wrong: [http://stackoverflow.com/questions/1270927/are-function-
stat...](http://stackoverflow.com/questions/1270927/are-function-static-
variables-thread-safe-in-gcc)

~~~
comex
Huh? The standard behavior in Python is to throw an exception if the key does
not exist.

~~~
gcr
I think rgarcia was talking about defaultdict and friends, see
[http://docs.python.org/library/collections.html#collections....](http://docs.python.org/library/collections.html#collections.defaultdict)

Classes that implement __getitem__ can behave however they want when you use
brackets on them. See
[http://docs.python.org/reference/datamodel.html#object.__get...](http://docs.python.org/reference/datamodel.html#object.__getitem__)

------
thedigitalengel
Firstly, half of the time the guy bitches about flaws with the compiler, and
not the languages (Page 0 to 6).

Most of the bitching is about language features which exist. If you don't like
auto_ptr then DON'T use auto_ptr! C++ at least allows you to ignore features
without paying a penalty. Which is why it has a virtual keyword; and does not
make every method virtual like Java.

The at() versus operator[] is also an example of the above: bounds checking
can get expensive in a tight loop.

It seems that the author has never seen a large, well-written C++ project. In
fact, that is the case with many such C++ bashers (I used to be one too). Once
one sees how C++ makes it easy to manage a 500000 line project, they will
probably think again.

~~~
Raphael_Amiard
> Firstly, half of the time the guy bitches about flaws with the compiler, and
> not the languages (Page 0 to 6).

That's pretty unfair. It's the language's complexity that makes the compiler
task almost impossible. Before clang came around, most C++ compilers had
really shitty error messages, gcc, icc, and microsoft's indiscriminately.

~~~
thedigitalengel
What I meant was that unless you're writing a C++ compiler, these reasons
should not longer bother you. Even if you are, you're free to leverage off
clang's lexer and parser.

Otherwise it is a lot like saying Java is bad because it is hard to write a
good VM.

------
Peaker
Many good criticisms, mixed with some bad.

Exception safety is achievable (pthread_mutex_lock example) if you wrap
resources with RAII classes. If he called this "difficult to use C libraries
while retaining exception safety" it would be more accurate.

The iterator criticisms are applicable to C iterators too (represented by
pointers or user objects).

The various things `baz = foo->bar(3)` could mean are interesting. Some of
them are reasonable, if you assume some sort of consistency. It may make
_audits_ hard, but the fact it might use implicit cast converters there should
not make it harder to read if you are allowed to make the most basic
assumptions about what an automatic cast operator would do.

I enjoyed it, nonetheless.

------
andos
Some of the examples at the end are laughable.

    
    
        If new[] throws an exception, this leaks 
        a ﬁle handle and deadlocks the next caller.
    

Poorly written code is buggy? No shit, Sherlock! — to paraphrase the OP.

The Frequently Questioned Answers has good, insightful criticism of C++ is
here: <http://yosefk.com/c++fqa/>

------
KonradKlause
BTW: The errata: <http://www.fefe.de/c++/>

------
comex
> With C++, you don’t see the writes, because it says
> `some_func(whatever,the_int,SOME_FLAG);` and it can still write the_int if
> it’s a reference. In C, it would be &the_int, which is easily recognizable.

I don't understand why people do this. C++ has not abolished pointers; it's
still possible to require the &, and it increases readability considerably;
why ever use references (to value types; objects are different) as function
parameters?

~~~
arghnoname
reference to const gives the benefit of passing a pointer, efficiency wise,
but prevents the called function from modifying the contents it points to.

~~~
jpr
AFAICT, that's achievable with pointers, too.

~~~
arghnoname
Of course it is, with the minor syntactic addition of callers passing in the
memory location. References aren't a _necessary_ feature of function
parameters and if the reference is going to be modified, a pointer is more
clear and should be used.

But there is a utility in allowing references to exist in the language for
overloaded operators, so you can overload a subscript in a collection and
assign to it, such as

foo[x] = bar, where foo is a collection of bar objects.

They could have restricted references to just these sorts of use cases and
when using foo[x] as an argument, require the user to type &foo[x], but once
references are in the language allowing them as parameters seems like a
reasonable enough decision to me.

------
huhtenberg
I tend read all "C++ is (bad|good)" lists and it never occurred to me to look
at operator overloading as something inherently bad, something that sacrifies
the clarity of the code for its brevity (see page 23 and onward). This is an
excellent angle... but not that I would prefer C++ to _not_ support
overloading.

------
albertzeyer
Many of the complaints have become irrelevant or at least much less relevant
with C++11 and huge progress also on the compiler side (most notable clang but
for example GCC also has improved many of the unreadable error messages).

~~~
Jach
I don't use nightlies, but last time I checked even clang didn't have great
template error messages. C++11 doesn't address his complaint of ever-changing
standard (though I don't think that's really serious). Does C++11 support lazy
extensions? (Last I checked it didn't. Use case: "Unless someone declared
operator&&, then both are evaluated.")

I think most of the complaints are still valid even with C++11. Whether the
complaints are worth bothering about is another issue: C++ is hard, yeah, it's
not going to get easier. Personally I wouldn't ever start a new project in
C++, I'd rather use C or Go or Obj-C if I need low-level speed and there's a
plethora of high level languages (that can still connect with C if needed) if
I don't. I nevertheless have to know C++ (non-11) for all the older projects
out there, most of which probably won't move to standardize on 11 making 11's
benefits moot.

~~~
jerrya
"I nevertheless have to know C++ (non-11) for all the older projects out
there, most of which probably won't move to standardize on 11 making 11's
benefits moot."

Yep, lots of projects out there that if I am lucky have moved up to Centos 5.
When they get C++0x where all of these issues are fixed is just this side of
never.

Oh wait a minute. Did I write C++0x? I mean when they get C++11 when all of
these issues are fixed.

------
dhruvbird
> Iterator to stale element (resized vector, or balanced tree after rebalance)

I don't think that rebalancing the tree invalidates iterators (other than the
deleted element [if any] that is)

------
Groxx
After seeing some of those rants, especially after the namespace-hatred, I
wonder if they'd prefer a flavor of Assembly. Apparently they hate many common
methods of simplifying and organizing code - clearly, things like virtual
methods should always look different than non-virtual methods, namespaces are
pointless when you can `gl_open`, and all macro-like functionality is simply
obfuscation.

------
jfr
Most, if not all, of his arguments were already beaten to death. The rebuttal
should be floating around somewhere on the Internet, but I couldn't find it
with a quick search right now.

Anyways, right at the beginning of the presentation you see the quality of his
arguments as he uses a very known problem of a particular compiler (GCC) to
bash the language. Sort of a straw man fallacy.

~~~
kjksf
His argument is that C++ is so complex that it's extremely hard to write a
fast, reliable compiler that generates good error messages.

The fact that it took gcc programmers 10 years to get to a point where gcc
doesn't use ridiculous amounts of memory when compiling few lines of tricky
code is a fact in favor of the argument, not against it. Gcc programmers are
smarter than an average programmer. If it was easy, they would have gotten it
right on the first try. Accordingly, C or Java or C# or Go compilers didn't
have such problems so C++ is more complex for compiler writers than pretty
much every other known language.

The same goes for error messages. In the past decades we've had tens of C++
compilers. If clang is the first one that is able to generate decent error
messages for templates, then it means it was a hard problem to solve.

None of that got any easier: if you want to write another C++ compiler, it'll
still be hard for you to generate decent error messages or compile tricky C++
code reasonably.

~~~
jfr
I could use your argument to describe pretty much any modern compiler or
framework. "It is extremely hard to write a fast, reliable Java SDK that
generates good error messages.", for example.

It took years to GCC to arrive where it is because it contained A LOT of
legacy code that have been accumulating since its first versions with C++
support. GCC is a compiler that evolved little by little through long years,
and many features were added with little design, to fix punctual problems,
which made it difficult to converge to a clean implementation.

Clang, on the other hand, had a good deal of proper design to write a clean,
robust compiler. The fact that clang reached where it is in such a short time
compared with GCC is a good evidence that it is possible to write a good C++
compiler with nice performance and error reporting if you employ good
engineering efforts.

~~~
kjksf
No, you couldn't.

No Java compiler has ever generated the kinds of error messages that every
single C++ compiler did for template errors. Feel free to give me an example
of Java compiler error message that takes 2 screens and fails to give you
useful information.

If you compared the complexity of clang C++ front-end with complexity of any
Java compiler front-end, you would see a huge difference in complexity (size
of the code, time it took to develop).

The first checkin in clang code base was in 2006-06-17. It took almost 5 years
and more than 31 thousand checkins to get to current state which is: not done
(<http://clang.llvm.org/cxx_status.html>).

A fully functional, fully compliant Java compilers have been written in
fraction of this time by much smaller dev teams.

The facts do not support your claim that "proper design" is enough to write
fully compliant C++ front-end in a reasonable amount of time. 5 years of
effort of top-notch, highly paid developers to not achieve the goal is not
reasonable and it's fully the result of C++ complexity.

------
dhruvbird
> x[0] -= x[1]; // x[1] adds sizeof(bar) here, not sizeof(foo)

This code doesn't even compile?? What is the author trying to show with this
example?

------
mattgreenrocks
Please flag this article. Why do we feel the need to discuss how much C/C++
rocks/sucks repeatedly? HN should frown upon bikeshedding more than it does.

If I wanted an echo chamber, I'd be on Reddit.

~~~
nasmorn
Very true. I don't think articles like this fall under pg's definition of
deeply interesting. They always draw crowds though. When I see one I have to
summon all my strength not to read it.

