> Could turn it around as "everything you can do in C++ you can do in C with a lot less language complexity".
No, you can't, C is lacking a lot that C++ brings to the table. C++ has abstraction capabilities with generic programming and, dare I say it, OO that C has no substitute for. C++ has compile-time computation facilities that C has no substitute for.
Is there an example of the generic programming that you've found useful?
The extent of my experience has been being able to replace functions like convert_uint32_to_float and convert_uint32_to_int32 by using templates to something like convert_uint32<float>(input_value), and I didn't feel like I really got much value out of that.
My team has also been using CRTP for static polymorphism, but I also feel like I haven't gotten much more value out of having e.g. a Thread base class and a derived class from that that implements a task function versus just writing a task function and passing it xTaskCreate (FreeRTOS) or tx_thread_create (ThreadX).
Typed compile-time computation is nice, though, good point. constexpr and such versus untyped #define macros.
The generic algorithms that come with the C++ standard library are useful. Once you get used to using them you start to see that ad-hoc implementations of many of them get written repeatedly in most code. Since most of the algorithms work on plain arrays as well as more complex containers they are still useful in embedded environments.
std::array can sometimes give you the best of both worlds for stack allocation in that you statically constrain the stack allocation size (no alloca) while guaranteeing that your buffers are large enough for your data. You can also do a lot of powerful things with constexpr that are just not possible with arrays. It is very convenient for maintaining static mappings from enums to some other values.
You've never used a template for a data structure and you've never used a destructor to free memory?
> C++ has compile-time computation facilities that C has no substitute for.
The substitute for this is that C is insanely easy to generate. Do your compile time computation in your build system.
OO is also pretty trivial in C -- the Linux kernel is a great example of a program in C that is very Object Oriented.
My point is trivially true as far as computability goes, but that is not what I ment.
All those abstraction capabilities can be a big detriment to any project, because they always come with a cost, and runtime is far from the only concern.
Specifically in an embedded project, toolchain complications and memory use (both RAM and code) are potentially much bigger concerns than for Desktop applications, and your selection of programmers is more limited as well; might be much more feasible to lock your developers onto acceptable C coding standards than to make e.g. "template metaprogramming" a necessary prerequisite for your codebase and then having to teach your applicants electrical engineering.
Both object oriented programming and compile time computation is doable for a C codebase, just needs more boilerplate and maybe a code-generator step in your build, respectively. But that might well be an advantage, discouraging frivolous use of complexity that you don't actually need, and that introduces hidden costs (understanding, ease of change, compile time) elsewhere.