
C++ and the Culture of Complexity (2013) - contrarian_
http://blog.greaterthanzero.com/post/58482859780/c-and-the-culture-of-complexity
======
notacoward
The bullet list near the end closely matches my own experience with C++.
There's an inordinate number of features creating an even worse profusion of
edge cases where they interact. Then the "solutions" for those edge cases
usually add even more complexity. Worse, they force programmers to coddle
their compilers. The ratio between what a C++ compiler will accept and what it
will produce sane code for is _huge_. That's why every C++ codebase I've ever
seen is full of code to do things that shouldn't be necessary, and goes
through all sorts of contortions to avoid doing things that should be OK, lest
the compiler spew out an un-debuggable mess.

I'm perfectly happy managing my own complexity in C, or avoiding it entirely
in Python. C++, on the other hand, seems to have been designed by compiler
writers for their own enjoyment and/or job security. Every other "systems
programming language" from D to Objective-C to Go to Rust to Nim presents a
more coherent face to the programmer.

~~~
WalterBright
> C++, on the other hand, seems to have been designed by compiler writers for
> their own enjoyment and/or job security.

Being a C++ compiler writer (Zortech C++, Symantec C++, Digital Mars C++) I
can assure you this is not true at all.

As to why C++ is so complex, my opinion is it is because it was designed a
long time ago, what is considered better practice in designing languages has
moved on, and C++ is unwilling to let go of the old decisions.

(There's always some major and vocal user who has build their entire store
around some ancient feature.)

For example, why does C++ still support EBCDIC?

~~~
Santosh83
THIS is the reason IMO too. C++ has taken on the _very_ difficult task of
remaining broadly compatible with C and with legacy features while at the same
time has continuously evolved over the decades, incorporating whatever was the
state of the art at that time, _without_ new features breaking old code.
_That_ is not an easy task without increasing complexity.

~~~
pjmlp
The book "Design and Evolution of C++" is quite interesting in that regard.

For all its warts, C++ only got adopted inside AT&T and later by almost every
C compiler vendor, because it just fitted on their existing toolchains.

Even lack of modules is related to that, C++ object files needed to look just
like C ones.

Now that C++ is grown up and can live on its own, it needs to pay for the
crazy days of its parties going out with C. :)

~~~
vram22
>The book "Design and Evolution of C++" is quite interesting in that regard.

I found that book very interesting in many regards. I had bought and read it
several years ago (out of interest, though I have not worked on C++
professionally).

Stroustrup goes into a lot of details about the reasons for many design
decisions in the language. While I'm aware that C++ has some issues, I was
really impressed by the level of thought etc., that he shows in that book,
when he talks about all the reasons for doing various things the way he did
them.

------
bjorn2k
I agree that c++ is complex, but remember that while trying to update the
language the standard committee is trying not to do breaking changes. If you
can do a greenfield implementation like rust, you don't have the baggage of an
installed base, yet. If you compare c++ to a language that is also around for
more that 25 years it starts to make more sense why it is complex. Add to
that, that it started as an extension to c, which is the reason why it became
popular in the beginning. Also, zero-cost run-time abstractions.

It is whatever you give priority design-wise, and if you look at what is
important in c++ (zero-cost runtime abstractions, control of the resulting
code), then ending up with something as complex as the c++ language is hard to
prevent.

~~~
IshKebab
Yeah by keeping backwards compatibility they avoided something like the Python
2/3 mess which I would say is worth the effort and cruft.

~~~
jVinc
Looking at the current programming language space, it honestly doesn't look
like the Python 2/3 issue hit them that hard in the long run. And while it
wasn't popular it might have been the right way forwards. Python is one of if
not the most rapidly growing language. In large part due to the data-science
movement which for anyone I've talked to is based on Python 3. I still
occasionally talk to 2.7 proponents, who explain to me that they need to have
this and that to handle all the complicated edge cases and problems in dealing
with bytes vs. strings... Issues that you just don't encounter at all in
python3.

If I had the choice of having a C++2020 with absolutely no backwards
compatibility but a greatly cleaned up language or having yet another step
down the path it's heading now, I'd chose the former even if it breaks
backwards compatibility. But the language is going to follow the developers
and they want to continue down the path where they get to explain again and
again and again the intricacies and delicacies of rvalue semantics, exotic
template meta-programming and what-ever the next big thing is going to be. All
the while we seemingly still don't even have a clear road-map for providing
simple modules.

I was enthusiastic back when C++11 hit and it felt like a great big push going
on in the language development, I had the feeling that things were really
going to take off... but now it feels like it just died out, and now there's
nothing that really gets me exited about the future of C++. I'm worried It's
going to get more and more complex and less and less used.

~~~
oblio
I know I'm in dangerous territory here, but what if the C++ community, as in
committee, compilers, etc., pull a Javascript strict mode?

Basically subsequent C++ compilers provide the possibility of some subset of
C++ which is considered modern. Then files can be marked with it, at which
point the C++ compilers reject deprecated idioms. Over time this should take
over, I think most of the JS in the wild is now using strict mode.

I can't be the first person to think of this. There must be a reason something
like this hasn't gotten traction.

~~~
gpderetta
The header inclusion model (where you will be textually including hundreds of
thousand lines of random code in a translation unit) doesn't make it easy to
have version flags.

Also many of the issues are intrinsic to the way the language has evolved (the
template compilation model for example), it is not just a matter of
deprecating a few features here and there (although that might help)

~~~
lowbloodsugar
Headers are what need fixing first. They are why I would never consider using
C++ for any new project and avoid jobs that require maintaining C++ code. C++
is the steam power of computing and keeping legacy monoliths running doesn't
do anyone any favors. That shit should have been piece-by-piece rewritten by
now.

~~~
carlmr
You're forgoing all of the sweet cash that C++ legacy maintainers will make in
20-odd years. Like the COBOL people right now.

~~~
gpderetta
Hey! That's my retirement plan!

------
ndh2
Lots of posts here frame it like "Yes, it's a bit of a complex language, but
the C++ committee has the difficult job of keeping everything backwards
compatible, so it's understandable." But that's not the reality. The reality
is that they (or Stroustroup) made that decision, and that decision was a
mistake.

The correct way to deal with backwards compatibility issues is the way Go does
it, namely to write tools to automatically upgrade code to a newer API [1].
And as that smug Google guy explained here [2], they have something like this
for C++, too. They just don't bother sharing it with us peasants.

The fundamental mistake of C++ is to religiously keep backwards-compatibility
with everything for all eternity. It's so easy to say that backwards-
compatibility is a good thing. It's obvious! But ultimately that decision is
the reason for all the problems that C++ has.

[1] [https://golang.org/cmd/fix/](https://golang.org/cmd/fix/)

[2]
[https://www.youtube.com/watch?v=tISy7EJQPzI](https://www.youtube.com/watch?v=tISy7EJQPzI)

~~~
gpderetta
Backward compatibility with C semantics and tooling was fundamental for C++
success. We wouldn't be having this discussion otherwise as nobody would have
used C++.

Tools are great for minor upgrades of APIs and syntax, but key to C++ was
compiling with existing system headers (no, realistically you do not want to
maintain your own 'fixed' version), and most importantly OS and library ABI.

~~~
pjmlp
AT&T had a Java like variant of C, before Java was even a thing, called C+@.

This DrDobbs article in an 1993 issue dedicated to possible successors to C,
is the only proof that it ever existed.

[http://www.drdobbs.com/article/print?articleId=184409085&sit...](http://www.drdobbs.com/article/print?articleId=184409085&siteSectionName=cpp)

Print view is the only way to read it properly.

------
yason
While my pet peeves might be different than the author's I agree that C++
makes you work very hard to keep things simple and produce something that is
beautiful. Just "avoid the complexity you don't absolutely need" doesn't
really work: some basic things are so fundamentally convoluted that I rather
just write idiomatic C because it's simple and doesn't get in my way. I want
the language to originate from simple axioms so that I can concentrate on the
complexity of the problem I'm solving instead of wasting brain cycles on
battling the complexity of the language. I would like to like C++ but its
benefits barely zero out the extra hoops.

~~~
thebear
Author of the blog post here. I think you just made me aware of the true and
deeper reason for my discomfort with C++: it's the fact that the convolutions
are in the _basic_ things. I am perfectly willing to put up with convolutions,
annoyances, bugs, weaknesses etc. in anything I use, be it a programming
language, a piece of software, a gadget, whatever. Nothing is perfect, and who
am I to criticize others for the imperfections in their work. What annoys me
is when the annoyances are in the basic things, in the most commonly used
features. Make the basic things reasonably good, and keep the crap off to the
side.

~~~
mannykannot
I think that one reason the convolutions are in the basic things was the
principle that C++ should be a superset of C, which is nominally a value-
semantics language, but which, with the use of pointers, very commonly (and
intentionally) leads to code with aspects of reference semantics. One example
of this is that C++ adopted The C practice in which arrays decay to pointers.

In addition, C++ not only inherited C's static-typing rules, but strengthened
them, and complicated them with inheritance (multiple, and polymorphic or not,
according to your needs), function overloads and operator overloading. Now add
generic types through templates, and the cases to be handled explodes to the
point where you need a Turing-complete parser.

A good case can be made for each of C++'s features individually, which leads
to justifications of the form "As we want both X and Y (and without
sacrificing run-time efficiency), then there really isn't any alternative to
[insert counter-intuitive issue here]..."

That it works at all is a testament to the skill of the people involved. The
complications that remain seem qualitatively different than the sort of
gratuitous inconsistencies that you find in PHP, for example.

------
pjmlp
Well, most of his points regarding complexity are not C++ specific.

The thing with equals applies to most languages that allow to redefine
operators.

Even Java has gotten quite complex, I bet he would have quite an hard time
succeeding at one of those quiz questions certifications (SCJP and friends).

And simplicity can also turn into more complexity, manual memory management in
C vs C++, "templates/generics" in Go, libraries to work around language
limitations in JavaScript (npm, yeoman, gulp, grunt, webpacker all at the same
time).

------
ajeet_dhaliwal
In my experience this disease afflicts more than just some C++ programmers,
the immaturity is boundless. I’ve noticed some colleagues even think there
must be something wrong if code looks simple.

------
ioquatix
Yes, C++ is a complex language. Yes, it has a ton of warts if you know where
to look. But, you can write beautiful software with it, and it can even look
beautiful too.

Yes, there are alternatives, but when it comes to performance, expressiveness
and actual deployability, C++ is pretty awesome.

~~~
lallysingh
Meh. The performance is almost never worth the huge complexity jump. Instead,
profile an app in a higher level language and if any part's too slow,
implement that in C/C++. That'll quite often be a low, single digit percentage
of the app. Get the performance for substantially less engineering/maintenance
cost.

~~~
foo101
C++ is a complex language no doubt. But software written in C++ need not be
complex.

Java, on the other hand, is a simple language. But the kind of unnecessary
complexity I have seen in Java-land (EJBs, Spring, etc.) has no parallel in
the C++-land.

So going by your argument, I would choose C++ over Java to avoid the
complexity jump, then profile the app, and if any part's too slow, improve
that again in C++.

~~~
WalterBright
Java just pushes the complexity into the user's code. For example, a friend of
mine once gushed to me about Java IDEs - with one click of a button, a hundred
lines of boilerplate are automatically added!

I replied a good language shouldn't need boilerplate code automatically
inserted.

~~~
WillReplyfFood
Boilerplate bureacracy usually happens, when Configurations are not
itteratable and/or a language does not enforce the providing of meaningfull
defaults. I find both sides guilt and charged here...

~~~
WalterBright
Metaprogramming enables insertion of custom boilerplate without need of an
IDE.

------
KayEss
Part of the problem here is that OO sits nicely with references, but C++ (like
C) is a value based language. This can be seen clearly in most of the design
of the standard library -- it's all value semantics, and as such relatively
simple and obvious to use (so long as you're not trying to cram OO into it).

A line like

    
    
        if ( a == b )
    

In C++ is pretty obvious what it does. It'll always be a value comparison, and
if a and b are pointers to objects you are comparing the object identities and
not their values. The meaning is exactly the same in Java of course, but the
fact you're dealing with pointers is hidden so you generate a lot of confusion
about the correct use of `==` and `.equals`.

The author certainly isn't wrong about a culture of complexity in certain
elements, but, to be hones, I see that everywhere else too and it needs to be
fought wherever it occurs.

~~~
pjmlp
Object Pascal, Eiffel, Ada, Mesa/Cedar, Oberon, Modula-2, Modula-3, Oberon,
Oberon-2, Oberon07, Active Oberon, Component Pascal, Sather, Swift, D, Go,
Rust are OO languages[1] and value based as well.

[1] - As usual, there are many ways of doing OO, not just C++/Java style.

~~~
KayEss
There are, but they all have at their core an idea of an object which has
identity, which values don't have. You can easily model objects in value
languages, but sometimes it isn't so easy to do the other way around (like
Java's strings -- Smalltalk had similar problems).

~~~
pjmlp
> There are, but they all have at their core an idea of an object which has
> identity, which values don't have.

Not at all, objects need to be explicitly allocated on heap, otherwise they
are value based.

I took care not to list any language that only allows objects as references.

~~~
KayEss
Argh, I misread what you wrote. It's very late here now :)

------
tines
It seems like this article is essentially complaining about the language
giving you too much control (e.g. in his copy assignment operator example),
when that's exactly what its goal is.

------
pfultz2
> What does that do? It makes the variable x refer to the object that y is
> referring to

In Java/C#, it doesn't always:

int x = 1; int y = 2; x = y;

The variable `x` does not refer to the object that `y` is referring to(as
there is no reference involved at all).

Assignment is a procedure that makes `x` equal to `y` without modifying `y`.
However, if `x` refers to `y`, then we can modify `y` after assignment to `x`.
This destroys the ability to reason locally about the code, because each
object essentially becomes as good as a global variable.

Even though C++ has areas where things gets complicated, this is the one thing
that C++ keeps very simple and consistent. There is only one definition of
copy, assignment, and equality, whereas in java there is multiple
definitions(deep copy vs shallow copy, `==` vs `.equals`).

> That’s called value semantics, although I would prefer the term state
> semantics: objects do not have a value, they have state, and that’s what’s
> being transferred here.

No. Its value semantics, as these objects represent some entity. The
interpretation of the state(or datum to be more precise) is the known as the
object's value. For example, when copying a `std::vector` the internal state
of the vector will be different on copy, as it will point to new piece of
memory, however, its value will still be the same.

> But experience shows that making a copy of an object is hard.

The compiler already generates a copy constructor and assignment for you. Its
only necessary to write one, when one is dealing with low-level pointers.
Using the standard built-in types and containers, writing a copy constructor
is never needed.

------
NTDF9
There are two kinds of developments:

1\. Application level: Typically manipulating lots of strings and data
massaging. I prefer Java or python for this. The IDEs and eco-system just is
so much faster to start with

2\. Systems level: Typically a high-performance system like a DB manager or a
fast processing library like a message producer etc. These things are time
critical and need performance.

I used to really love C++ but I agree, it takes far too long just to start
making things run. Sigh!

------
sevensor
When I was young, I thought I understood C++, because I had been taught it in
school. I did not. Having interviewed several recent graduates who thought
they knew C++, I believe this is fairly common, particularly among people with
advanced degrees in engineering and the sciences. It's very easy, in C++, not
to know the scope of one's ignorance.

~~~
int0x80
This is very true. I've seen very ignorant (on c++) people say that they dont
find c++ complex at all, but very simple.

Most of the time this only shows the lack of deep knowledge on the language.

~~~
pjmlp
You can do the same with someone that says C is easy to master.

Just pick a copy of ANSI C and randomly ask a couple of UB questions.

You have a catalog of about 200 cases to chose from.

------
neo2006
The author is relating a personal experience As a former C++ dev I saw some
wonderful code base written in Ç++ and some horrible code base, as any other
language. The only specifity Ç++ have in my opinion is that some features have
a high learning curve and are not necessary shared with other languages. Once
you mastered Ç++ it's easy to move to other languages but it's hard to do the
reverse path.

------
airstrike
It takes balls to imply that one may use Java in lieu of C++ so as to reduce
complexity.

~~~
thebear
Author here. Quote from the _first_ paragraph of my blog post: "The decision
to implement the math backend of GreaterThanZero in Java was driven by other
considerations, primarily the appeal of Google App Engine as a hosting
platform."

------
jokoon
C++ is the only language that is compiled to binary, compatible with many
compilers and OS, and can do both low and high level constructs.

It is true that it is a complex language, but this is true for every tool that
allows you to do so many different things.

The complexity is not a goal in itself, it's just that it can do all of those
things if you need them.

Simple tools are great and will save you a lot of time, but the truth is that
you won't always be able to do everything with them. C++ allows you to do
everything, and it is true that the cost can be very high and requires a lot
of thorough knowledge and expert learning of what happens, but there are
projects and cases where you just need to use C++ because that's the only
choice you have left.

I agree that an alternatives like rust or D would be great as replacements,
but the problem remains: if compilers are not mature on most platform, and if
you don't have a large programmer base because the basics of the language are
not simple enough, the language won't grow.

~~~
steveklabnik
Rust uses LLVM, so we share the same characteristics here as clang does.

------
bogomipz
The author states:

>Neither the performance issue that move semantics address nor the perfect
forwarding problem exist in classic OO languages that use reference semantics
and garbage collection for user-defined types."

I understand reference semantics but what are move semantics?

Also what is the "perfect forwarding problem"?

~~~
thebear
Author here. A few years back, I wrote an article about the issues that you're
asking about. Since the article still comes up as the #1 result of Google
search for "C++ rvalue references", I believe it's ok for me to recommend it
here:

[http://thbecker.net/articles/rvalue_references/section_01.ht...](http://thbecker.net/articles/rvalue_references/section_01.html)

Warning: Read only if you have a serious interest in C++.

~~~
bogomipz
Thanks, I thought this was well-written.

------
Animats
One word: Boost.

C++ went off into template la-la land some years back. C++ templates were not
supposed to be a programming language. But they turned into one, and not a
good one.

Look up the implementation of "max" and "min" in C++ templates.

Now there's a push for "move semantics", to keep up with Rust and the cool
kids. But not with language support and a borrow checker. With templates and
cute tricks with the type system.

------
andrepd
>Rvalue references provide move semantics, and they solve the perfect
forwarding problem. Neither the performance issue that move semantics address
nor the perfect forwarding problem exist in classic OO languages that use
reference semantics and garbage collection for user-defined types.

I don't get this. The performance issue move semantics solve doesn't exist if
you just use automatic garbage collection? Is this what the article is saying?

~~~
gpderetta
Those specific issues do not obviously exist if the language lacks value
semantics. Other issues of course do exist, namely allocation overhead and
pointer chasing and the difficulty of doing escape analysis.

------
beached_whale
I think it depends on where you sit on the stack. A library can take a lot of
the complexity away from the higher levels in C++ code. The user code can look
fluent and understandable. On the implementation side of the library being
used(depending on where it sits in abstraction) is where some ugly complexity
shows. But this also generally reflects the competence of the authors.

~~~
humanrebar
On the other hand, it's hard to grab a half dozen C++ libraries off the shelf
and just use them together without issues.

~~~
beached_whale
True. That is getting better though and lots of people are putting time into
solving that. It's tricky because no one is talking about the same thing
either. Like my project structure isn't your's.

It's not byte code so distributing binaries is a crappy problem too. I am of
the mind that a dependency system above the build system(s) is probably the
best bet. Not as low level as binary interface, but I need libary X >= version
n.m.o...

------
carapace
I think there's a "sweet spot" in complexity (at least for software): too
complex gets rejected, but being too simple lacks traction. I think artifacts
like C++ memetically infect the brain better than a more elegant PL might,
exactly because they require you to think about them a lot, but you can still
get things done and not be (totally) overwhelmed.

------
mar77i
Isn't there anyway some of a pendant to Conway's law in this? I mean, C++'s
standard library is - or at least used to be - rather complex, so the people
that would go there and use C++ for their project - would more likely than not
- carry at least some of that complexity over into the project...

------
petters
I like the fact that in C++, the line

x = y;

behaves the same if the types are int and vector<int> (unlike e.g. Java and
Python).

~~~
sireat
Except you are not always assured on what = does when reading other people's
code. :)

~~~
petters
Well, you can define a function in Java called setFoo, but that instead sets
bar. Similar.

You can intentionally confuse in most letters if you want to.

------
geoffroy
Does anybody know a good tutorial or book for learning modern C++ 17, focused
on best practices (new language paradigms, TDD/BDD, etc.) ? Something like
Michael Hartl's Ruby on Rails Tutorial

------
commandlinefan
Well, if you haven't encountered any brain-bending Javascript, you haven't
gotten very far into Javascript.

------
otabdeveloper1
C++ is not more complex that its competitors like Haskell or Rust.

~~~
tomohawk
Having used c++ for many years, this does not come across as a ringing
endorsement for looking into Haskell or Rust.

I'm quite happy with my current gig using Go. Looking back, the culture of
complexity surrounding c++ is obvious, but talking with my peers who have only
ever done c++ - it's like they have Stockholm Syndrome.

~~~
tandr
Any language (or tool for that matter) that you have to invest heavily in, and
use for a while will produce a symptoms of "Stockholm Syndrome". Moreover,
once you are hard pushed to switch from it to something else, withdrawal
symptoms are quite pronounced too.

Go has its own share of idiosyncrasies, and it drives me nuts sometimes even
more than C++ did, but these bursts of mental grind are less common and much
shorter in comparison :). The arcane complexity of C++ (and tooling around it)
is something that I don't miss at all.

~~~
PopsiclePete
I sometimes like to dabble in C++, late at night, while sipping some whisky.
For short periods of time.

Last night, I decided to "practice" writing iterators. Specifically, I wanted
to write a class that i could use in a new-style C++ range loop that would
give me all files in a directory. Useless but fun practice, I thought.

I had Bjarne's latest book by my side, and the final draft of the C++17 spec
in open in a PDF, and it took me a good 2-3 hours of trying before I "got" it.

I'm still not sure I'm doing it correctly, btw. It does compile without
warnings, and it works, but I can tweak the iterator function signatures in
seemingly incompatible ways and it _still_ doesn't complain and _still_ works,
even when I swear it shouldn't. "Ok, surely by doing this I'll break it!" \-
nope, still works.

I don't love everything about Go, and once in a while I'll wish for more
expressiveness, but I was never, ever, as 10% confused with it as I am with
whenever I attempt to do something seemingly trivial in C++.

