Comment by drob518

Comment by drob518 16 hours ago

22 replies

About 28 years ago, I figured out that I’m just not smart enough to use C++. There are so many foot guns and so much rampant complexity that I can’t keep it all straight. I crave simplicity and it always felt like C++ craved the opposite.

lefty2 11 hours ago

c++ 03 was a lot easier.

For instance, if you want to avoid unnecessary copy operations when returning a string, just return it in variable that you pass by reference (eg. void doSomething(string& str);) likewise avoid the vector class making unnecessary copies, simply by creating the objects on the heap and use a vector of pointers instead of values. It's a bit more ugly, but it works, and you don't need to read a 24 page blog to understand all the corner cases where it can go wrong. modern c++ is all about syntactic suger.

  • usefulcat 8 hours ago

    Agreed that c++03 was much simpler, but that doesn't change the fact that there are useful things that are possible in modern c++ that simply were not possible before.

    Like if I have a vector<std::string>, in c++03 when it resizes it must copy every string from the old storage to the new storage. For a vector of size N, that's up to N+1 allocations (allowing for the possibility that std::string uses the small string optimization).

    Granted, std::string doesn't have to allocate when copied if it's a "copy on write" implementation. IIRC, there were some implementations that used that technique when c++03 was the latest, but I don't think there are any that still do, due to other problems with COW.

    In modern c++, that same vector resizing operation requires exactly one allocation (for the new vector storage), because all the strings can be moved from the old storage to the new.

    Yes, you could have a vector of pointers to std::string, but now you've got yet another allocation (and indirection on access) for every string. In practice that tradeoff almost never makes sense, unless perhaps the strings have shared ownership (e.g. vector<shared_ptr<string>>).

    Ultimately, I think there's really no question that the vector resizing optimization described above is useful in certain scenarios. Having said that, I do agree that the associated complexity is annoying. Therefore, the real question is whether it's possible to have these benefits with less complexity, and I personally don't know the answer to that.

fenwick67 11 hours ago

I write c++ for a living and I feel the same way. And many c++ codebases have that OOP AbstractObjectInterfaceFactory stink which makes it even worse

epx 14 hours ago

I understand the individual rationales of C++ things but I lost the faith on the whole thing.

  • chihuahua 11 hours ago

    The way C++ has developed over the past 20 years seems similar to someone starting with an algorithm that fails for some edge cases, and patching the behavior with a different hack for each edge case, which breaks other cases, then patching those, and on and on forever.

    • ryandrake 7 hours ago

      I think the way to be successful with C++ is to 1. Pick a sensible subset of the language that you allow in your project, and ban everything else. How much that subset should include is a valid debate and reasonable people can disagree, but I don't know of any successful C++ project that just YOLOs every part of the language into the project. And 2. (related) Pick the earliest possible standard that your team can live with, and don't give in to the temptation of cherry-picking anything from a future standard. For instance, the decision of switching from C++14 to C++17 should be a major debate full of fistfighting.

      • okanat 44 minutes ago

        Things start to break apart when you have dependencies that adopt newer standards or use broader features. There is only so much you can do unless you would like to reimplement libraries like SKIA, doctest, Qt6 or any modern game engine. It gets worse with security and updates. At some point a library will require a newer standard otherwise you have to adopt the entire codebase and assume the entire responsibility of all security updates.

        At that point you are slowly rewriting the universe. So you can also do it in Rust tbh (which provides seamless updates and integration between epochs/editions).

benreesman 15 hours ago

Systems programming in the large is hard, owning the category for decades harder still.

Even languages that have tried to fast-follow and disrupt C++ end up looking a lot like C++. There is an irreducible complexity.

  • zbentley 14 hours ago

    I hear this a lot, but I don’t really understand how this manifests in language complexity like the stuff in TFA in practice.

    Like, I can understand how systems programming requiring programmers to think about questions like “how can I proceed if allocation fails? How does this code work in an embedded context with no heap?” is hard and irreducible.

    But I can’t understand why a language’s choice to impose complex rules like C++ move constructor hell is an inevitable outcome of irreducible complexity in systems programming. Put another way: C is also a systems programming language that works for many people, and it doesn’t have any of these Byzantine rules (unless you build them yourself). That’s not to say C is better/preferable, but it swims in the same “official Big Gun systems language” pond as C++, which seems to indicate that revalue semantics as complex as C++’s are a choice, not an inevitability.

    • HarHarVeryFunny 13 hours ago

      I wouldn't say issues like this are dues to irreducible complexity, but more symptomatic of long-lived languages that continually get extended but don't give up on backwards compatibility. It's basically the 2nd law of thermodynamics applied to programming languages that they will eventually die due to increased entropy.

      Maybe if move semantics, and noexcept, had been designed into C++ from the beginning then the designers might have chosen to insist that move constructors be noexcept, but since these were added later there is code out there with move constructors that do throw exceptions...

      Note by the way that the issue being described isn't strictly about std::move or move semantics in general, but more about the STL and containers like std::vector that have chosen to define behavior that makes noexcept move constructors necessary to be used when reallocating.

    • kanbankaren 11 hours ago

      > But I can’t understand why a language’s choice to impose complex rules like C++ move constructor hell is an inevitable outcome of irreducible complexity in systems programming.

      Programmer here for 30 years in C/C++. It is true that C++ has become a more complex language after rvalue references were introduced, but you have to understand the rationale behind C++: a language suitable for large scale systems programming with *ZERO OVERHEAD*.

      The language complexity especially rvalue references was to reduce overhead. Pre-C++-11, there were many code patterns that involved constructing temporaries and destroying them immediately.

      C is not suitable as a large scale programming language. Just look at the number of defects in the Linux kernel and their attempt at extending the language through custom compiler attributes to overcome the limitations of C.

      • LexiMax 7 hours ago

        > but you have to understand the rationale behind C++: a language suitable for large scale systems programming with ZERO OVERHEAD.

        Is this the reason why C++ was created, or the last remaining niche that C++ is holding onto?

        I remember the early 90's, and it very much seemed like C++ was being pushed as both a general-purpose language and the logical successor to C, insert Linus Torvalds rant here. On top of that, C++ made the decision to privilege a form of polymorphism that had pointer-chasing baked into its internal design, as well as having a good chunk of the standard library being considered a footgun best to avoid due to how much it blew up compile-times.

        I think that C++ is a zero-overhead language now because a series of general purpose languages that came afterwards took the other niches away from it, plus the benefit of 30+ years worth of compiler optimizations that were originally largely aimed at the mountain of C code that was out there.

        EDIT: Almost forgot about exceptions, the other enormous performance footgun that was an early pre-standard C++ feature.

      • hn_go_brrrrr 3 hours ago

        C++ doesn't have zero overhead, though. The committee is unwilling to take ABI breaks and so have left performance on the table. For instance, unique_ptr<T> can't be passed in registers but T* can.

        Zero overhead is a fiction the committee likes to tell themselves, but it's not true.

    • usefulcat 7 hours ago

      > I can’t understand why a language’s choice to impose complex rules like C++ move constructor hell is an inevitable outcome of irreducible complexity in systems programming

      It's not about irreducible complexity in systems programming, it's about irreducible complexity in the creation of higher level abstractions.

      You could certainly implement something functionally equivalent to std::vector<std::string> in C. What you couldn't do in C is implement std::vector<T> correctly and efficiently for any type T. That's where much of the complexity comes from.

      The hard part is giving the compiler enough information so that it can automate a lot of what would have to be manually written in a language like C, and to produce a result that is both correct and efficient.

    • cjfd 14 hours ago

      The difference is that in C one is supposed to do allocations and deallocations oneself. Then move semantics is just pointer assignment with, of course, the catch that one should make sure one does not do a double-free because ownership is implicit. In C++ ownership is indicated by types so one has to write more stuff to indicate the ownership.

      • SJC_Hacker 12 hours ago

        > The difference is that in C one is supposed to do allocations and deallocations oneself

        No, you should only use the heap if necessary.

        The bigger issue in C is there is no concept of references, so if you want to modify memory, the only recourse is return-by-value or a pointer. Usually you see the latter, before return value optimization it was considered a waste of cycles to copy structs.

        In the embedded world, its often the case you won't see a single malloc/free anywhere. Because sizes of inputs were often fixed and known at compile time for a particular configuration.

    • jesse__ 10 hours ago

      As you pointed out, the idea that a systems language requires some high level of complexity is just straight-up wrong, and demonstrably so (see, C).

      The best programmers I know of have basically all abandoned C++ in favor of either languages they made, or just use plain C

  • drob518 14 hours ago

    I have no problem with systems programming issues. That complexity is essential complexity inherent in the problem itself, regardless of language. I have a problem with C++’s accidental complexity. I find C much more tractable. It certainly has a few of its own footguns, but it has much less accidental complexity.

    • SJC_Hacker 12 hours ago

      As the author of the FQA noted (Yosef K-something), in C++ its more the combinations of features which causes so many issues.

      And here we see this principle rear its ugly head yet again. In this case, its the combination of exceptions, manual memory allocation and the desire to make things work efficiently - of which the move constructor was developed as a "solution"

groundzeros2015 15 hours ago

Same. I’ve read all the books. Written all these things at least a few times. It’s just not doable post C++11.

FpUser 12 hours ago

C++ is a universal tool with long history. So yes it makes it very complex for various reasons. However it does not preclude one from being productive. I do not come anywhere close to being expert in C++. Still write software that blows the shit out of competition. I have general understanding how the things work and when I need some particular feature I just look up the efficient way of doing it in whatever language. Not just for C++. I actively use many languages. My goal is to deliver good software and get paid by happy client, not to know every little detail of the tools I use, it is just impossible and serves no useful purpose.