1. 18
  1.  

  2. 11

    I love criticizing C++; it’s become one of my favorite languages, but its flaws are extremely apparent. This “fqa” lists a lot of actual valid criticisms, but many of them are at best outdated, and many have probably never been correct.

    Here’s a few criticisms I agree wholeheartedly with:

    • The grammar is incredibly complex.
    • The compilation model, complex grammar, and lack of compile-time encapsulation make build times incredibly slow.
    • The type system is complex (which isn’t an issue in itself, a powerful type system is great, but error messages usually show insanely long type names).
    • There’s a lot of duplicate facilities; lots from C which are mostly-superseded by newer C++ features, and lots from new C++ features replacing old C++ features (scoped_guard replacing lock_guard, etc).
    • And more; C++ is far, far from perfect.

    Here’s some of the stuff that’s just wrong or outdated though:

    • The FQA keeps implying that garbage collection isn’t less efficient than C++‘s RAII. In many cases, it’s probably almost true, but in a lot of places, GC pauses just aren’t acceptable. Also, most of my interaction with Go over the last few months has been related to fighting the GC.
    • Saying that “C++ manual memory management is inherited from C without changes” couldn’t be further from the truth. Granted, in the past it might have been true that people wrote C++ like C but with new/delete instead of malloc/free, but in anything resembling modern C++ code, you never need to use raw new/delete in practice. Thinking of ownership, and expressing it using the type system, turns out to be a really powerful paradigm for managing memory.
    • The “No high-level built-in types” point is outdated. You can, in fact, write std::vector myvec{1, 2, 3} or std::map<std::string, int> vec{{"a", 1}, {"b", 2}, {"c", 3}}; these days.
    • A lot of the gripes about unmanaged languages in general are way less relevant now that we have great tools like the address sanitizer (and valgrind, although valgrind isn’t always practical given the extreme runtime performance hit).
    • The “defective operator overloading” point seems wrong. You have to return your results “by value”, but that “value” could be a struct with only a pointer and a destructor (which is what you get by returning a unique_ptr, and essentially what you get by returning a std::string). Nobody actually makes the “code copy massive temporary objects and hope to have them optimized out by our friend the clever compiler”. (Operator overloading-based string concatenation has its own set of issues, but not related to a lack of GC.)
    1. 4

      Saying that “C++ manual memory management is inherited from C without changes” couldn’t be further from the truth. Granted, in the past it might have been true that people wrote C++ like C but with new/delete instead of malloc/free, but in anything resembling modern C++ code, you never need to use raw new/delete in practice. Thinking of ownership, and expressing it using the type system, turns out to be a really powerful paradigm for managing memory.

      The problem is that C++ has no single model for memory management. It has multiple models, meaning you have to think hard thoughts about what happens when objects allocated different ways contain and refer to each other. This is part of C++’s greater lack of conceptual unity.

      1. 1

        You’re probably right in many situations, but it doesn’t match my personal experience of the language. In all code bases I’ve worked on, the assumption is that the destructor frees all resources owned by the object. That, combined with actually thinking about how your system’s ownership works, and utilities like unique_ptr, turns out to be really powerful.

        I don’t think I’ve ever written a large C program where I haven’t had to debug memory leaks, but I still haven’t written a C++ program with a memory leak (and I would’ve known, because I always run my C or C++ programs with -fsanitize=address and/or valgrind while developing or testing).

        Note that when I say “the destructor frees all resources owned by the object”, I don’t mean that I have personally written destructors which free resources. Sure, that’s occasionally necessary, but in the vast majority of cases, my classes’ heap-allocated memory is managed by std::unique_ptr, std::vector, std::vector<std::unique_ptr>, or other RAII-style types, and I don’t have to even define a destructor.

    2. 6

      This looks like it hasn’t been updated since before c++-11. While plenty of it is still quite valid, I’d be really curious to see how much has changed in recent years.

      1. 2

        At first glance, the FQA section is largely still up to date; obviously, none of the new features are asked about, but the answers to the existing questions mostly look accurate and complete.

        Some improvements have been made regarding constructors, particularly forwarding constructors, which would probably warrant slight modifications of some of the answers.

        Perhaps unsurprisingly, a couple of the issues listed in the defects section have been addressed:

        No way to locate definitions

        This one is basically a complaint about the lack of module system. Modules are being added in C++20 and are supported by some compilers already. I haven’t used them yet myself and don’t know much about them so can’t really say much about the specific points mentioned.

        No high-level built-in types

        The lack of syntactic support for higher-level types (you can’t initialize std::vector with {1,2,3} or initialize an std::map with something like {“a”:1,“b”:2} or have large integer constants like 3453485348545459347376) is the small part of the problem.

        This is now largely possible.

        Cryptic multi-line or multi-screen compiler error messages, debuggers that can’t display the standard C++ types and slow build times unheard of anywhere outside of the C++ world are the larger part of the problem.

        clang made leaps and bounds of progress over earlier compilers in this regard, to the extent that I hardly run into the compiler output vomit problem at all anymore these days.

        1. 3

          I think, for example, 6.1 is different now. Post C++11, and moreso post C++14, consistency across compilers has drastically improved in my experience.

          For 6.3, I think the need for recompilation cycles has been reduced, their length has been reduced, and it’s much easier to deliver a C++ interface now than it was in 2009.

          For 6.4, templates have gotten easier, compilation times shorter, and post LLVM especially, error messages are significantly clearer.

          I can’t go through them all point-by-point, but in other cases the standards process and documentation has definitely improved since the first ISO standard, and many of the gripes seem to be with that.

          But my point isn’t really to challenge this FQA… I’d just be really interested in seeing an examination of it for the last 3 versions of the standard to see how the facts on the ground have improved or deteriorated.

          I’ve only been able to move to (compilers that support C++11 and later) over the past year or so, so both my agreement with the criticisms here and my perception of the improvements is very likely distorted.

      2. 5

        A classic…back when I wrote a lot of C++ for indie gamedev efforts with friends, this was as useful to us as any of the (excellent) Scott Meyers books.

        I wish more languages had equivalents–just really unflinching and opinionated responses to “best practices” in a language.

        1. 2

          This is such a great list of C++ questionable answers. I do agree it’s a little dated, but this is a great read! All the opinions about OO are pretty accurate too.

          Object-oriented programming is the best known way to develop complex systems. It was invented because customers kept demanding increasingly complex systems.

          1. 1

            As others have said, a lot of this is obsolete (or just mistaken) FUD.

            I had bad experiences building big systems in C++ in the ‘90s and ‘00s, and very reluctantly returned to it in 2014. The combination of C++11, Clang, and the Address & Undefined Behavior Sanitizers has made it so very much better. My code is more efficient, the compiler catches many more bugs and tells me about them with comprehensible messages, and Bad Stuff like buffer overflows and use-after-free gets caught immediately so I can debug it.

            I’m still curious about the various new hotnesses like Rust, but I’m happy in my day job cranking out C++17 code.