For me, constexpr for resolving complex build-time logic into constants, cleaner type handling for the math library makes it harder to accidentally pull in a 64-bit libm when we only have a 32- it FPU, and templating for sure.
It's pretty easy in C to reach a point where macros of macros is madness but templates calling templates still works with the debugger and is a powerful mechanism for hoisting complexity out of loops. Also works nicely for writing outer loops once in c++ calling a template specialized to either inline assembly or c++.
Managed pointers sometimes but our team seems pretty good with object lifetimes from a C background, and we don't use too much heap anyway.
RTTI and exceptions we often disable to save code space.
RAII is a little tricky without exceptions, we often use it's easy.
With templates, I use them for generics, but also judiciously to create meta-linguistic abstractions to get around certain limitations of language. For example, I built an ORM using them.
I don't use auto, or lambdas, or even foreach (I use iterators), despite oddly having much more dynamic language experience.
I believe people, in general, agree C is too simple of a programming language to actually deal with all the essential complexity of modern software development in various cases.
The actual complaint people have about C++ is that it has too much extra unnecessary accidental complexity due to weird ways features interact.
The analogy of the Surgeon I believe should be adjusted having these two points in mind. Generally speaking, surgeons would agree that "scalpel and whiskey" are too simple of a tool set for doing modern surgery. However, if you take modern surgeon tools and add a bunch of weirdness to them, then surgeons would reject them on the grounds of being more complicated than needed.
> A bad surgeon could also be overwhelmed by the amount of modern tools and use the wrong one.
When this is contrasted with the C++ situation, I believe the comparison would be fair only if all the modern features in C++ had to do with the inherent essential complexity of software development. That is not the case (at all). Expert developers have been talking about how C++ is unnecessarily complicated for ages (before C++03 even; back when things were simpler in terms of weird language feature interactions).
I agree with your overall point, but I feel like RAII is practically the key feature that C++ provides over C (:
Or at any rate, I wouldn't say it is anything to do with "modern C++". It was there from the beginning, and C++'s object lifetime rules are pretty core to the language IMO.
I'm curious, since you phrased it that way: what C++ feature(s) do you tend to reach for first, when moving from C to C++?
C++ has tons of features, many will say "too many", and it accumulates over every revision. C, by comparison, is simple.
But sometimes C is too simple, it is fine for 99% of your needs, but for the extra 1%, you need C++. If that's the case, just write C and compile it with a C++ compiler so you can take that extra feature you need. Just because you are using a modern C++ compiler doesn't mean you should start using RAII, managed pointers, exceptions, and all these things.
Sometimes it makes sense to go full-on "modern C++", but using C++ literally as "C incremented by one" is a use case that shouldn't be dismissed.