In Defense of C++
10 points by rcalixte
10 points by rcalixte
This is not a particularly inspired defence of C++, mostly just rehashing the standard argument of “if you write C++ well, it isn’t bad” from different angles.
But this site: a developer’s personal site, but with a GDPR popup where hundreds of companies are asking for permission to use my data, and ads that defy my adblocker and cover the whole screen between every other paragraph? What is this?
I just discovered the blog today and had none of those annoyance issues. I currently mainly use Firefox-based browsers, a decently tuned uBlock Origin setup, and a few other tools.
I’m on Firefox on Android with uBlock Origin on mostly default settings, but that’s pretty much always enough for me. It was genuinely really surprising seeing ads - an adblocker is the first thing I install on a new browser, and I’d forgotten how bad they can get.
I tried turning off uBlock for that page, clearing cookies for it, and refreshing, but I don’t see anything?
It might be something on my end but I just checked on mobile and also wasn’t able to reproduce the annoyances. I’m also located outside of the EU. Sorry I can’t be of more help.
No worries, it’s not the worst problem in the world and I can easily avoid this site. I was just very surprised to see a site on Lobsters, especially a developer’s personal blog, be so aggressive with its advertising!
No offence to the author, but I can’t help but strongly disagree with most of their points and feel like a lot was elided to paint the criticism of the language undeserved.
So, as a developer, how are you to know which approach is the best one for your specific use case? Surely you have to have a deep understanding of the language to make these decisions, right? Not really… I mean, don’t get me wrong, it helps, but it’s not a hard requirement
I’d argue you absolutely need to have an understanding about how C++ does stuff to know what to avoid.
You need to know how to use managed pointers (or that they even exist in the first place and that not only are raw pointers usually not necessary, but you rarely even need to use new
/delete
, also have you heard of this new thing called references?). You need to know the conventions of the standard lib. And if you want to know how to read modern code, you need to be aware of an ever growing list of features.
C++ is Hard to Read
Then don’t write it that way.
This completely sidesteps the issue of joining an already established team / project. Especially if said project is old and uses stuff from before the nicer parts of C++ were standardized / implemented.
You can also write complex and unreadable code in C++ if you want to. It’s all about personal or team preference.
I feel like there are part of the language that are difficult to intuitively learn without grokking, even if your team is otherwise disciplined. Declaring functions either = 0
or = delete
, what virtual
is, the intricacies of the class system, it’s all stuff that you have to memorize and/or keep a good reference handy.
You don’t have to use all the features of C++ if you don’t want to.
No, but again, you need to be aware of most to be productive. I feel like the best thing that could happen to this language is if they threw out half of it and kept a sane subset. But of course that can’t really happen because it’d be in conflict with the intended goals of the language.
C++ has a large ecosystem built over the span of 40 years or so, with a lot of different libraries and tools available. This can make it difficult to know which libraries and tools to use for a specific task. But this is not unique to C++; every programming language has this problem.
What the post avoids to mention is that adding a dependency is a non-trivial task. With, say, Rust, you can just declare your needs in a TOML file and Cargo figures it out for you. Same with Go or JS and many other languages. Meanwhile with C++ you’re most likely stuck with libs being pulled in using version control and then cobbled together using CMake. It works, but it’s a far greater skill floor.
The blog wrote:
You can write simple, readable, and maintainable code in C++ without ever needing to use templates, operator overloading, or any of the other more advanced features of the language.
And that made me think something very similar to your response. If you don’t use templates, that means that you aren’t using any of the standard collections or smart pointers. And then you’re using the worst possible subset of C++: C with weirdness.
Re: safety, the whole point of Rust’s approach to memory safety is to outright preclude entire classes of bugs. You could waste years tripping over the myriad footguns in C++ and figuring out all the bad/risky ways of doing things, or you could just use a language that makes it hard to do bad/risky things. Memory bugs happen to everyone in C++, even people who “know what they’re doing” and use all the right tools and abstractions. It keeps happening. This is the same hand-waving we’ve been seeing for years.
You can write unsafe code in Rust, you can write unsafe code in Python, you can write unsafe code in JavaScript.
Uuuh no you can’t? If we are talking about memory safety, in rust only if you type unsafe
, and in python and JS only if you do FFI magic.
C++ is riddled with footguns, and all the learning materials will point you towards them more or less severly.
What if we are not talking about memory safety? I write a combination of C++ and Python at my day job and I dread the weeks where I’m on Python duty because the code will be so much buggier.
With C++, I can write auto area = box.width * box.height
and be fairly confident that I’m done with the code. In Python, I’ll write area = box.width * box.height
and suddenly my code breaks in production four months later because
box.width
was changed from a value to a property and the underlying function deleted a necessary folder in the user’s directory.box.width
stopped being 125
and became {"magnitude": 125, "units": pixel}
.Though not in the literal instance of calculating an area, these are both real regressions that I’ve experienced in this project. You might say that we shouldn’t write Python in a way that introduces these bugs, and you’d be right. However, that still feels like C++ zealots saying that you just shouldn’t write C++ in a way that causes bugs.
To prevent misunderstanding, I’m not saying that C++ is a good language. It’s not in my top five and everyone on the project would be excited if we could get the time and funding to rewrite in something better. In light of the memory safety issues with C++, we also go out of the way to ensure that it operates in a safe manner (e.g. zero network access, no PII). On the other hand, the Python code has deleted user files, had cross site scripting vulnerabilities, and leaked personal information. It just never did it from a memory leak.
What if we are not talking about memory safety? I write a combination of C++ and Python at my day job and I dread the weeks where I’m on Python duty because the code will be so much buggier.
I’m not sure if anyone here would defend Python on those grounds. I also find Python supremely annoying when it comes to not writing code with fewer bugs, and much prefer Rust for anything serious. I do wish there were more good “high level” options for writing correct code though.
Now is that because of Rust? I’d argue in some small part, yes. However, I think the biggest factor is that any rewrite of an existing codebase is going to yield better results than the original codebase.
Well frankly, the biggest factor in reducing memory leaks is the borrow checker. When people cite the reason for less memory leaks in a rewrite, this is why. Sure, you can catch design mistakes during a rewrite. This claim rests on the fact that developers will have to be cognisant of the memory allocations during the rewrite, while the borrow checker enforces it.
These claim rests on the fact that developers will have to be cognisant of the memory allocations during the rewrite, while the borrow checker enforces it.
I agree. This omission downplays why rewrites often reduce leaks, and borrow checking (or GC) is a major factor. Still, memory management, like most debates, rarely boils down to a single neat answer. Solutions can mitigate the problem, but the nuance never really goes away. For example, I’m not aware of any borrow checker or GC that can make semantic memory leaks, where resources are held too long because of design choices rather than mechanics, disappear.
However, I think the biggest factor is that any rewrite of an existing codebase is going to yield better results than the original codebase.
Haha.
Now is that because of Rust? I’d argue in some small part, yes. However, I think the biggest factor is that any rewrite of an existing codebase is going to yield better results than the original codebase.
Funny, other people claim that rewrites are always buggier regardless of the “from” and “to” languages
I was stopped cold when I read this:
C++ is very old, in fact, it came out in 1985, to put it into perspective, that’s 4 years before the first version of Windows was released
I know this is pedantry here, but Microsoft Windows was first released in 1985, Windows 2.0 in 1987, and Windows 3.0 in 1990. I know computer history isn’t really taught, but it wasn’t hard to find this information.
I write C++ for a living and I don’t think it simply boils down to “skill issue”. It seems to me that C++ is complex for complexity sake, whereas Rust brings a kind of complexity that rewards the programmer with safety. But apart from the language itself I think the committee lacks a clear vision of what the language should and, more importantly, what it should NOT be. The bloat only grows as features keep getting added. Of course, many of those features are highly requested but most of the time the implementation is weird or leaves a lot to be desired. Plus it takes years for those features to land in all the compilers, delaying adoption even further. All in all, the need to constantly “defend” C++ says a lot.
That’s how I feel when I see these companies claim that rewriting their C++ codebases in Rust has made them more memory safe. It’s not because of Rust, it’s because they took the time to rethink and redesign their codebase and implemented all the lessons learned from the previous implementation.
Well, this is easily testable. I would like to see companies rewriting in C++ and see how it results.
But the easiest and most straightforward way to make C++ safer is to simply learn about smart pointers and use them wherever necessary. Smart pointers are a way to manage memory in C++ without having to manually allocate and deallocate memory. They automatically handle the memory management for you, making it much harder to have memory leaks or dangling pointers. This is the main criticism of C++ in the first place.
This completely misses the point of memory safety. Memory safety is not about deallocation, Rust even has a safe memory leak function. Main issue is dangling pointers, unauthorized memory access etc. And smart pointers don’t prevent any of this. You can still easily get dangling pointers by passing a reference to a stack object into a smart pointer. Searching for shared_ptr segfault
in any search engine yields many results.
There are of course cases where C++ is the best choice, but IMO this article is very poor at defending C++.
To properly use & understand the standard library, you need to understand most of C++’s complexities around things like references, templates, constructors/destructors, etc. Same knock on Rust honestly, but there really is no comparison to something like Python.
C++ is “Outdated”The language has some pretty fun shiny new features yes. But the author really dismisses the developer experience part of the critique with a “¯\_(ツ)_/¯ works for me” attitude. Maybe worth getting into more in a later section though.
C++ is “Unsafe”Yes, C++ can be made safer; in fact, it can even be made memory safe
No, sorry! It’s been proven time & time again that static analysis is simply not enough to make large C++ codebases bug-free. “Just use smart pointers” is OK general advice that quickly falls apart in practice. Canonical example is reference invalidation, which can be caught in simple cases by static tools but not all.
C++ is Hard to ReadNo comment, I find it fairly straightforward as well.
C++ has a confusing ecosystemHere we go, it’s later! Without relying on an IDE, please explain how to create a new C++ project. Ok got all that? Here’s Rust: cargo new [project]
. Here’s Go: go mod init [project]
. Here’s Javascript: npm init [project]
. Even Python, another historical mess, finally has uv init [project]
. Having a consolidated package ecosystem + build tool is effectively a requirement for new languages nowadays. C++ has no canonical build tool and a whole bunch of fragmented package ecosystems, mostly ones for operating systems.
No comment, I think the rest of the article is fairly unobjectionable.
C++ is worth defending, but this ain’t a good defense.
Perhaps the best defense of C++ today is that it’s a better alternative to C. No one should be coding in C anymore.
I still haven’t found an ideal successor, I.e. a systems language that doesn’t rely on GC:
I’ve ended up compromising on GC. I like Kotlin a lot, I just wish it could free itself from more of Java’s limitations, and that its build system weren’t an unholy nightmare.