Hacker News new | past | comments | ask | show | jobs | submit login
Nobody Understands C++: Intro (2007) (emptycrate.com)
38 points by _tk_me 10 months ago | hide | past | favorite | 52 comments



> The comment in question begins “My take on C++ is that the best programs only use a fraction of the features.” The commenter further states that he is weary of operator overloading and templates.

> In my experience, people who make comments like the above tend to be people who think they are C++ experts but who are actually only novices. I do not know the commenter personally, so I’m not trying to say anything about him specifically.

> I only have 4 years of experience with C++

... I am only a C expert, but I agree with the Slashdot guy. After spending years as a believer like the author, I realized all this class-ification effort was pure waste, since most of it would never be reused. Same with all the idiomatic conversions. Don't get me started on boost.

I withdrew from the cult and get just as much done with high quality C in half the time.


I went from "c++ fondness" to "c++ distaste" over the years. The newest c++ standards fix a lot of issues, but at the same time increase complexity. The backwards-compatibility nature is the classic double-edged sword. If we could avoid it, c++ would be at least significantly nicer (not really less complex).

That being said, a good team using a dab of c++ will beat "high quality C" any time of the year for me. Getting away from the preprocessor alone is huuuuuuuge. Everybody complains about memory safety, and the truth is that with even the dumbest string/buffer class it's actually pretty trivial to do the right thing. c++ _does_ really fix most of the c issues IMHO.

A big problem IMHO is that we "learned" to use c++ properly late. The newest standard revisions came late. The feature abuse it receives is still somewhat rampant, and yes a newcomer to c++ would probably find a use for every single feature in a simple usage scenario.

Reading large c++ codebases is hard due to the intermixing of old/new and simply different styles. It has a huge cognitive load, and that is my primary reason for the growing distaste: I want code to be clear and coherent. I can take the complexity if that's used consistently, but it can change massively between even just PARTS of a project.


> It has a huge cognitive load

This. Its like trying to keep three distinct languages (C, old C++, new C++) in one's head at the same time. It is much better use of one's mental energy to spend that effort on three actually distinct languages that are, e.g., tuned to different domains and use cases.

Imho the C++ community needs to address this or eventually become irrelevant as people gravitate to more sane and user-friendly ecosystems. Not sure how that can be done without breaking some eggs. My preference would be for a simplified and streamlined C+++ that tackles this complexity head on.


> or eventually become irrelevant

That's exactly what it should do. Stop adding features, keep the standard stable, let new languages slowly take over. The C committee's approach is quite close to what I would like to see.


In my opinion there are two C++'s:

1) the version used by people writing things like the STL/Boost

2) the version used by people writing apps.

A huge amount of the updates to the language are to make the lives of people in category 1 easier.

The people who fall into category 2 really don't need to use all the template headachery that the language allows. They can if they want, but they probably don't have to.

I've noticed that the Boost people are now deprecating old versions of C++. I look forward to hopefully quicker build times. To be fair, the Boost codebase is a masterpiece of working around decades of C++ versions and compiler bugs/missing features. Obviously that makes the code largely incomprehensible to newcomers.


Templates are amazing at helping to extract performance from things by moving run time decisions to compile time when done well. I usually only go there when the profiler points me somewhere is either acting as a bottleneck or something requires to perform better. You can then even decide to go even further through branching to cases you can go into SIMD intrinsics.


I believe the same is applicable in other languages. Complicated types and class structures quickly become unwieldly, it's much better to use simple types and not rely on polymorphism too much.


> I realized all this class-ification effort was pure waste, since most of it would never be reused.

I don't think this is a valid take. Classes are not about reuse at all. They are about encapsulation and specifying types/providing interfaces.

> Same with all the idiomatic conversions.

What's your complain about idiomatic conversions?

> Don't get me started on boost.

Why is Boost, or any library at all, relevant in a discussion about C++? Should our opinions on C++ vary if we mention POCO, Abseil, or even Qt?

Frankly, I don't think your personal takes are well informed. It sounds like you struggled to use something different than something you knew expecting that you could simply keep writing the same code in any other programming language, and subsequently started complaining that other languages are not like the one you used. That's hardly a critique of any other language, and instead it's insight on how developers who succumbed to crystallizing their expertise end up suffering from their inability to adapt.


> Why is Boost, or any library at all, relevant in a discussion about C++?

I don't have a dog in this fight, but I've been following the scene since early 2000s, a lot of boost made it into the standard. FYI.


> (...) a lot of boost made it into the standard. FYI.

Some components that have been proposed to be admitted to the C++ standard were also contributed to Boost, but that's about it. There are far more components in Boost that were never even considered for the standard than the ones that were.

Nevertheless, the relevant part is that Boost is not nor it ever was C++, or representative of C++. Conflating third-party libraries and frameworks with the C++ programming language is at best a pointless straw man.


> Some components that have been proposed to be admitted to the C++ standard were also contributed to Boost, but that's about it.

Reality is: boost libraries were created, widely adopted, and once interfaces stabilized, we got technical reviews from wg21. Not the other way around.

So the point is moot.


Yes, but it is for the better. Although it had the side effect of making boost less relevant.


> Why is Boost, or any library at all, relevant in a discussion about C++?

It’s what the article uses to make its point. Did you read it?


Worth noting that the article is from 2007. As of C++11 (from 12 years ago) you can write the equivalent code with just C++ and the standard library (range-based for loops, std:function).


Class composition is about reuse. Class inheritance isn't.


That may be the case in practice but it is not literally true, since semantically, inheritance is effectively a form of copy-and-paste in the compiler. It is literally about reusing components written elsewhere. I would argue that both are forms of code reuse, with the latter also having consequences for type checking.


> That may be the case in practice but it is not literally true, since semantically, inheritance is effectively a form of copy-and-paste in the compiler.

No, not really. The whole point of inheritance is declaring new types by implementing specifying interfaces in a way that specify type hierarchies.

If your goal was code reuse and "copy-and-paste" then nothing would beat composition. If you decide instead to go with inheritance, you have far more requirements to meet that go way beyond code reuse.


> get just as much done with high quality C in half the time.

Bullshit. C++ may have plenty of warts, and sometimes people go way OTT with templates (90% of Boost), but there's no way you're writing code as fast in C as C++ unless it involves no string manipulation, pointers, containers, etc.

String manipulation alone is so awful in C that there's no way you're right. But throw in smart pointers, collections, JSON, regex, etc... All of which are much easier to use in C++ and either built in, or in JSON's case, easy to add.

If you're literally just doing basic maths, then maybe. But how many programs are like that?


This. C++ is awful in many ways, but it can be much nicer than C for writing _application_ code. Use RAII, avoid deep class hierarchies, restrict yourself to the STL and use smart pointer types for lifecycle management. That’s it for starters, you will write much better code than in C.

Then you opt in to certain features like templates or operator overloading based on your bike shedding level.


> Bullshit. C++ may have plenty of warts, and sometimes people go way OTT with templates (90% of Boost), but there's no way you're writing code as fast in C as C++ unless it involves no string manipulation, pointers, containers, etc.

For many of those things, maybe he already has existing libraries (strings, containers) that have been tested in the field?

Sure, it won't make him faster than in C++, but his speedup could come from needing to allocate less cognition to the language and more to the problem he is solving.

Even expert C++ programmers, who have been doing it for decades, still need to lookup language stuff for C++ in the odd corners. Someone who hasn't been focusing on C++ in depth, but has instead been focusing on the problems over their career might be faster in C than in C++.

It's a trade-off (isn't everything?): lookup everything in the language as you develop vs lookup nothing, or already know a lot and develop faster vs know very little and develop slower.

(I use "know" to mean "knowing the language and libraries")


>Bullsh*t.

Please keep the quality of conversation high here. Thank you.


I find today's C++ extremely challenging to pick up speed for someone coming from let's say, Java. Smart pointers, pointer/references, rvalue reference, copy/move semantics, (perfect) forwarding, constructors, and how all that interacts with templates. It's just so unwieldy complex. It takes me hours to write something that would take less than a minute in Java even though I have experience with plain C. The worst thing is that after it finally compiles I feel I can't be sure if I've followed all the rules and best practices correctly or if it's going to blow up spectacularly and potentially unsafely at runtime. The fact that a book like "Effective Modern C++" is needed with all those traps and foot guns to be aware of is ludicrous.

And don't get me wrong - I think C++ is the most powerful language out there on the right hands - it just feels to require a lifetime of learning to became productive on it.


Yeah, I love C++ as a hobbyist programmer but will readily admit I basically use it like C but with the added convenience of strings & classes. Hardly how you're supposed to use it these days, but this is fine for my toy projects. But I can't imagine using the language professionally where you've got to be aware of best practices, modern enhancements, etc. Seems overwhelming!


coming from java you probably don't need all that stuff (and you should already know about constructors). you really need to understand the differences between call/return by value and by reference, and RAII - the rest can be left to the library writers.


I'm always saddened by the distinction between "user level C++" and "library level C++", but I hope it's gotten better over time. I remember once trying to write my own shared_ptr clone after the release of C++11, and having to learn as I went about why reference collapsing was invented was very, very discouraging.


i think the distinction exists in most languages - in the case of libraries you are trying to make things easy for callers of your code who's motivations you cannot understand; in the case of application code, you probably do have a pretty good idea of how/why people might call it or try to extend/maintain it.


> I find today's C++ extremely challenging to pick up speed for someone coming from let's say, Java. Smart pointers, pointer/references, rvalue reference, copy/move semantics, (perfect) forwarding, constructors, and how all that interacts with templates. It's just so unwieldy complex.

The main difference between Java and C++ in the features you listed boils down to a key difference between Java and C++: object ownership and lifecycle management.

Java's "let's just heap allocate the world and let the JVM sort itself out" allows Java developers to be oblivious to the need to actually think about the life cycle of any object at all. That's fine for some uses, and definitely most of Java's uses, but it's also something that prevents Java from even being considered an option in performance-minded applications. It also gets Java developers to develop incomplete mental models of how computers work. For example, a Java developer who does not understand pointers/references is also a Java developer who fails to understand Java's value and reference types. A Java developer who doesn't understand constructors has also more pressing matters to concern himself with.

In C++, there's a conscientious effort to ensure developers have full control over the life cycle of each and every single object ever instantiated throughout an app session. Smart pointers were added to provide clear semantics on how heap allocated objects should be owned and shared. Move semantics were added because developers want to transfer ownership of resources instead of having to deep copy objects around. These features are aimed at performance and memory safety in a language that by design allows developers to handle low-level details if they want to.

Then there's the backwards compatibility. Perfect forwarding is basically syntactic sugar to get rvalue references to work as expected.

> The worst thing is that after it finally compiles I feel I can't be sure if I've followed all the rules and best practices correctly or if it's going to blow up spectacularly and potentially unsafely at runtime.

The same goes for any programming language. You mentioned Java. I know people who work at an unicorn whose hiring process consists of putting together a Java web service, and they outright rejects people who present a project that hasn't been onboarded onto PMD or SpotBugs. We're talking about companies who hire experts and enforce code reviews, and they still enforce the use of linters and static code analyzers.


When I mention constructors I am talking about initializer lists and all the subtleties and special cases around them.

Basically most of the complexity I mention comes from memory management. It's just a lot of details to know about and keep in your head. It's the price to pay for the flexibility and power.

In Java you need to understand references and object lifecycles or you get memory leaks and slow applications.

Actually, I work on a performance sensitive application and it's written in Java and it's a good choice for it, since the performance comes from algorithmic complexity, mostly from asymptotic complexity. A C++ rewrite wouldn't significantly impact the performance of the application.


> Basically most of the complexity I mention comes from memory management. It's just a lot of details to know about and keep in your head. It's the price to pay for the flexibility and power.

That's basically the point: your complain is not really about C++. You're complaining about a computational problem that some programming languages abstract away with a heavy performance tradeoff. Programming languages that allow developers to manage memory will also force developers to think through memory management and object life cycles. This is not a problem caused by or specific to C++. That's the natural consequence of programming computers.

> Actually, I work on a performance sensitive application and it's written in Java and it's a good choice for it, since the performance comes from algorithmic complexity, mostly from asymptotic complexity. A C++ rewrite wouldn't significantly impact the performance of the application.

I'm sorry for being blunt, but if you think performance-sensitive applications stay at the algorithmic complexity level, you are definitely not dealing with a performance-sensitive application. You're just not writing naive code.

To go straight to the point, performance-sensitive applications are applications where low-level details, such as if memory is allocated at all, if an object is copied or not, and if some data is kept in cache, represent critical performance regressions. We're talking about details beyond cache locality, and keeping that cache hot is a critical feature. Algorithms alone don't cut it.

I recommend checking out the talk from Timur Doumler on low-latency C++ to see what I'm referring.

https://m.youtube.com/watch?v=EzmNeAhWqVs


I know very well what it means to care about a memory allocation cost or an atomic operation in a fast path. In some applications though, that is not the dominating performance factor, instead the algorithm dominates. For example, with C++ an manual memory management our application could take something like 58 minutes to run while in Java it would take 60 minutes. The impact of the low level management is entirely insignificant for performance in the big picture in these cases.

The performance comes from the big O complexity. Memory management is a constant factor in the algorithm.


> We're talking about companies who hire experts and enforce code reviews, and they still enforce the use of linters and static code analyzers.

I'm not sure I'm following, are you suggesting that experts don't need static analysers or linters? Or that enforcing linters is not in and by itself a good thing?


I won't argue that the first example is cleaner, but both seemingly simple examples have a major issue. Lifetime.

The observer pattern is notorious for being overly complicated in C++ because you have to ensure Observer and Observable keep valid references to each other.

"Modern C++" (what ever that is nowadays) leverages RAII (i.e. scope-based resource management) to improve upon the situation.

With your typical observer (i.e. signal/slots) library you get a "scoped connection" object when registering the callback function. You store that object in your class and when it is destroyed, it breaks the connection. Furthermore, it facilitates the implementation of correct copy and move semantics.

It's not that people don't "understand" C++, the problem is that there are so so many things to watch out for, even in simple use-cases, that even experienced C++ programmers make mistakes on a regular basis.


Funny note: Implementing e.g. a flexible observer pattern in Rust has the same ownership and lifetime problems. Usually this involves some weak reference, central broker, etc.

So, that's really not only a language problem, but rather the result of the pattern / data structure involved. I.e. cyclic dependencies not being very nice once you have to think about lifetimes.


TBH, C++ has changed so much that any opinion from 2007 isn't worth much today, and that applies no matter if you think C++ got better or worse since then ;)


Today is just tomorrow's yesterday.

I'll just avoid C++ for now, and wait for future articles to say that the C++ of 2023 was too complicated to use.


Furthermore the boost features used in the "better" example are now standard parts of the language and niether example looks good relative to "modern" C++17. (Though new problems have been introduced too, e.g. overuse of `auto`.)


It is still valid.. it would be invalid if C++ removed old features, but it doesn't.


Valid only in the sense of “we can improve on this old way of doing things.” Not valid in the sense that using boost for foreach and function objects would make absolutely no sense since C++11 which has those features built in, and with cleaner syntax.


Agree, and I suspect that a lot of things will also be deprecated by 2030 once modules get really integrated in the ecosystem.


Oh, this is Jason Turner! I'm sure he'd no longer write that kind of code, given its pre-C++11.

Not really intellectually interesting anymore IMO.

People say "I know some C++" and that could mean they have 2 decades, or 2 days, experience.

In reality, this is not a C++ problem, this is a software engineering problem. You need to learn to choose a solution that is good, not a solution that sounds good, or that will impress management.

If you need OOP, use it, otherwise, stay very far away. Same with callbacks and other funny constructs. You need them? Use them. You use them because they sound cool? It may be that you just want to call two functions after one another -- you dont need a callback to the first for that.


It is not that I necessarily disagree with the author but it does seem like he is making it a bit too easy for himself to make his point. Ever since C++ got lambdas, I would say that bind should be considered deprecated. It is more difficult to understand AND cannot possibly be more efficient.

The general point stands though. If you try to make everything as general as you can, you are going to make it more difficult for yourself than necessary. Beware of generalizing from one or fewer examples.


i don't think that std::bind is that difficult to understand - here's some stuff about it i wrote earlier: https://latedev.wordpress.com/2012/08/06/using-stdbind-for-f...


If your codebase supports C++20, then std::bind_front, std::bind_back are superior alternatives. Among other things, you then no longer need to use the _1, _2, ... placeholders if you want partial function application.

But surely GP cannot be talking about std::bind in the context of

> Ever since C++ got lambdas, I would say that bind should be considered deprecated

since both came about in C++11.

Given the context of the article, GP is most likely talking about boost::bind, which was intended as a replacement API for std::bind1st, std::bind2nd (which were deprecated in C++11 and removed in C++17), and largely fills the same niche that std::bind does (except with surprisingly many caveats / differences between the two).


C++ is very frightened of breaking existing code, including at the binary level. Some shared library built in 2007 stands a decent chance of running today if it avoided the ABI break of ruin and disaster where string and list changed slightly.

It's also widely considered a very complicated language and there's some worry it suffers in user acquisition as a result.

If you take those two properties together, the game plan of making the language look nicer without changing how it works is about all you've got. It accretes more stuff with a bias towards making some use case look nicer.

It leaves margin for faster code below it because slow choices are forever. More syntax over the same operations is a dubious interpretation of making things simpler.

Yet it lives on. People start new projects in it. Some people think the economics work out in favour.

Oh, and some people did understand it in 2007. Maybe only five but they did exist. Some of those may still be heavily involved enough to understand the current language, but I wouldn't be shocked if they're burned out and gone. We might be down to zero.


> Some shared library built in 2007 stands a decent chance of running today if it avoided the ABI break of ruin and disaster where string and list changed slightly.

At least with the default standard library implementation on Linux you can still select which version of these classes are used by setting the __GLIBCXX_USE_CXX11_ABI (or something similar) define. Both versions of these classes are still present in the implementation. So the old library can still be used despite that change.


C++ still has the preprocessor and all the horribleness that brings. It's also still a language where you need to know a lot of underlying details about how things work because you want to not be slow and you want to know whether or not you've just allocated memory that you have to manage. You need to know about integer sizes, how characters and integers are similar, pointers and dereferencing.

You can still leak memory easily and use after free, segfault and so on.

So IMO there's a cognitive load where you cannot ignore how things are happening as you might in a dynamic language. Stuff can be static or inline or stack based or heap based and all of it matters. C was bad enough but C++ just piled on more and I got tired of that really. Boost took a million years (subjectively) to turn into something decent and I remember not being able to use any of it because of being stuck on an old compiler on Windows.

I do hope we've moved on from having to build a lot of optimisation in by hand towards being able to describe a problem with it's constraints and limits and have the optimisation done for us.


> A fellow Slashdot poster has unknowingly made my point for me quite well, and it is what I will use to start this intro. The comment in question begins “My take on C++ is that the best programs only use a fraction of the features.”

Plot twist, that Slashdot poster was John Carmack </joke>


> Nobody Understands C++: Intro (2007) (emptycrate.com) .. including the fella that invented it /s

If C++ was a human language, then it would be like every new piece of writing consisted of brand-new nouns, verbs, adjectives and it's own syntax and grammar.


This piece is pre C++11 with in my opinion was the biggest change in C++.


(2007)


The author now has a YouTube channel on C++ with over a 100k subscribers: C++ Weekly with Jason Turner https://youtube.com/channel/UCxHAlbZQNFU2LgEtiqd2Maw


Added. Thanks!




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: