It depends on what you target. I do alot of Kernel and really low level C++, and it's a completely different style than even app dev, so you have to be sure you don't go down the completely wrong route.
For my target space, there's a few rules for c++.
Footguns:
A. No STL, it causes to much bloat when binary size is critical.
B. (almost) No inheritance or virtual functions. Again, it causes some bloat and adds in some instructions and code to support these features that can cause issues in low level environments.
Things to learn (All work in the kernel):
A. Learn templates, especially c++20. Templates add ZERO bloat, they literally just generate the function again with proper types the same way a macro would.
B. Use RAII.
C. Use constructors and overloading.
D. Zero pointers should be exposed to code except where absolutely needed. If you need to pass by reference, use the c++ style.
E. All allocations should be wrapped in a class with RAII. You can overload the operators on the class to make it seem like a pointer so you can still do if(!RAIIPointer).
A good video on kernel c++ is here https://www.youtube.com/watch?v=AsSMKL5vaXw . You can use a surprising amount of C++ language features in the kernel and it makes it extremely smooth and safe if you do it properly.
Yes! 100% Right, can't believe I left that out. One of the big things is to make sure to use ConstEval if you can do a new verson of c++(20), since ConstExpr isn't guaranteed and can leave stuff in the binary that can hurt size or obfuscation. For example one thing I see alot is lookup tables for hashing who use constexpr and hide some key data in there, but it "may" just randomly do it, and can cause alot of issues.
Bloat can also refer to the invisible infrastructure code that's compiled into a binary when using certain language features. C has a fairly linear relationship to the size of the compiled assembly, iirc, but C++ will vary wildly based on which features you use.
I think you missed exceptions often being a problem in low level and embedded targets. That knocks out most of STL anyway.
I also think you are a bit harsh on virtual functions - it introduces a single indirection, yes, but sometimes that is fully justified. RTTI on the other hand... of course depends a bit on target characteristics.
Perhaps controversial, I've also found (especially bare) lambdas and even std::function objects useful, although may evolve into something purely static when the dust settled. Highly dependent on target of course.
It will be interesting to see the final form of the new MISRA std, since the active one predates all of this.
For my target space, there's a few rules for c++.
Footguns: A. No STL, it causes to much bloat when binary size is critical. B. (almost) No inheritance or virtual functions. Again, it causes some bloat and adds in some instructions and code to support these features that can cause issues in low level environments.
Things to learn (All work in the kernel):
A. Learn templates, especially c++20. Templates add ZERO bloat, they literally just generate the function again with proper types the same way a macro would.
B. Use RAII.
C. Use constructors and overloading.
D. Zero pointers should be exposed to code except where absolutely needed. If you need to pass by reference, use the c++ style.
E. All allocations should be wrapped in a class with RAII. You can overload the operators on the class to make it seem like a pointer so you can still do if(!RAIIPointer).
A good video on kernel c++ is here https://www.youtube.com/watch?v=AsSMKL5vaXw . You can use a surprising amount of C++ language features in the kernel and it makes it extremely smooth and safe if you do it properly.