I don't think I need to say C++ has been used to build great things. The web browser you're using is very likely built in it.
But it's also been the security nightmare of the century. And been used to build the most horrible messy codebases in existence.
C++ encourages coding style that leads to very complicated execution flow. Multiple inheritance. What's the actual concrete method this thing is going to call? Fire up the debugger and set a breakpoint...
Operator overloading. Yes, there are good uses for it. Like math code. Or some container implementations. Unfortunately it's often just hiding what's really going on or worse, bugs. Yeah, you're so clever overloading /-operator for building file paths...
Exceptions - there to provide untold number of possible execution paths making a typical C++ program flow nearly impossible to comprehensively understand. Exceptions seem to discourage from actually handling the errors as opposed doing something about it at the call site. There's often little you can do five nested calls above, where the exception is caught. Handling means almost always a some sort of logging function or user notification, not actually doing something about it. It's easy to forget it's not only about resource management. Side effects matter too! RAII doesn't undo IO.
Those features hide bugs and make C++ maintenance a true challenge.
Of course one can earn a steady income writing C++...
> There are two kinds of programming languages: those that have been used to create horrible messy codebases, and those that haven't been widely used.
I think Bjarne's original statement, "There are only two kinds of languages: the ones people complain about and the ones nobody uses" [0], has been widely paraphrased.
> C++ encourages coding style that leads to very complicated execution flow. Multiple inheritance.
Can you bring another example that leads to "complicated execution flow" because literally no one uses multiple inheritance (the only exception being when a class needs to implement multiple interfaces).
> Operator overloading.
Very useful feature. Enables you to write readable expressions for actual mathematical objects (e.g. complex numbers, quaternions) and used to implement things like smart pointers. Yes, you should exercise caution when using it.
> Exceptions
Exceptions are useful if used to report errors, not to control program flow. Also, nobody "discourages" from handling errors - you should do it if you have enough context to properly handle the error, otherwise you don't have much choice other than to propagate the exception up.
Even single inheritance with multiple subclasses makes following the potential code paths mentally as many times harder as there are classes that extend or override the common base class.
Operator overloading; looks like we agree.
Exceptions do control flow, even if you just use them to report errors. They're often effectively hidden gotos.
> Even single inheritance with multiple subclasses makes following the potential code paths mentally as many times harder as there are classes that extend or override the common base class
But that is a property of inheritance and virtual functions, not something inherent to C++. You could be using another object-oriented language and the logic would be as hard to follow. We could discuss why or why not that property is problematic, but that would be straying from the topic, which is C++.
> Exceptions do control flow, even if you just use them to report errors. They're often effectively hidden gotos.
Again, same logic can be applied to any language that supports exceptions.
>Even single inheritance with multiple subclasses makes following the potential code paths mentally as many times harder as there are classes that extend or override the common base class.
Composition over inheritance is a technique used to reduce this problem.
You don't have to use runtime dispatch in C++. It's pretty easy to just grep and make sure no methods are virtual. You pretty much have to go to town with runtime dispatch with languages like Java.
On exceptions, the Ada people also came to the conclusion they're necessary and that return code checking is not viable.
OTOH, GCC and Clang provide warn_unused_result, and it's easy to define types for returning errors from functions that throw a compile-time error if ignored by the caller.
I find explicit error handling safer and more elegant than exceptions, especially if you consider that writing exception-safe generic code is hard and error prone.
>It certainly has its good points. But by and large I think it’s a bad language. It does a lot of things half well and it’s just a garbage heap of ideas that are mutually exclusive. Everybody I know, whether it’s personal or corporate, selects a subset and these subsets are different. So it’s not a good language to transport an algorithm—to say, “I wrote it; here, take it.” It’s way too big, way too complex. And it’s obviously built by a committee.
> Stroustrup campaigned for years and years and years, way beyond any sort of technical contributions he made to the language, to get it adopted and used. And he sort of ran all the standards committees with a whip and a chair. And he said “no” to no one. He put every feature in that language that ever existed. It wasn’t cleanly designed—it was just the union of everything that came along. And I think it suffered drastically from that.
> I would try out the language as it was being developed and make comments on it. It was part of the work atmosphere there. And you’d write something and then the next day it wouldn’t work because the language changed. It was very unstable for a very long period of time. At some point I said, no, no more."
> In an interview I said exactly that, that I didn’t use it just because it wouldn’t stay still for two days in a row. When Stroustrup read the interview he came screaming into my room about how I was undermining him and what I said mattered and I said it was a bad language. I never said it was a bad language. On and on and on. Since then I kind of avoid that kind of stuff."
Take a very low-level system programming language, add some OO syntax, then have someone who is avowedly anti-OO* add generics and templates, and then unleash it on hordes of junior programmers.
None of these is inherently wrong and "unleashing a programming language on junior programmers" is a a bit of a cliche that can be levelled at any language.
I get to maintain some nasty c++ code at work that was two decades in the making under a succession of (at least) two separate teams and is some freakshow of cautionary tales with occasional comments in Russian for comedy relief. And I still think c++ is one of the greats and will be around (and alive) in 30 years' time and I'm apprehensive of people painting it as some cautionary tale.
Individually they are defensible. In combination, the attempt was full of leaky abstractions that led to violations of the Principle of Least Surprise. In addition, going to a given shop, you never know exactly which subset of all the possible C++ subsets are in use. In that sense, there's no single language called "C++".
But yes, I admit the 'unleashed on junior programmers' was a bit of a cheap shot.
"People scream about efficiency, go on the web you read "efficiency efficiency efficiency!". No. They will actually love things that, that however slow, [will make] things convenient for them. And most researchers, really, when it comes to it, love inefficient languages that make it easy to write a n academic paper about how to improve it. Um, let's see, language myths: we want a language for writing reliable code. No! Most programmers, most of the time, scream in horror when they have to do something extra to for reliability, maintainability, safety, all the good stuff"
All hail ultraliberal interpreted properties-based dynamic languages!
But it's also been the security nightmare of the century. And been used to build the most horrible messy codebases in existence.
C++ encourages coding style that leads to very complicated execution flow. Multiple inheritance. What's the actual concrete method this thing is going to call? Fire up the debugger and set a breakpoint...
Operator overloading. Yes, there are good uses for it. Like math code. Or some container implementations. Unfortunately it's often just hiding what's really going on or worse, bugs. Yeah, you're so clever overloading /-operator for building file paths...
Exceptions - there to provide untold number of possible execution paths making a typical C++ program flow nearly impossible to comprehensively understand. Exceptions seem to discourage from actually handling the errors as opposed doing something about it at the call site. There's often little you can do five nested calls above, where the exception is caught. Handling means almost always a some sort of logging function or user notification, not actually doing something about it. It's easy to forget it's not only about resource management. Side effects matter too! RAII doesn't undo IO.
Those features hide bugs and make C++ maintenance a true challenge.
Of course one can earn a steady income writing C++...