Some features of C++ have implied run-time cost or can lead to significant code size increases. Which features should I consider avoiding when developing for a bare-metal resource constrained microcontroller? And why?
Look at the MISRA (Motor Industry Software Reliability Association) guidelines for safe C. They were designed specifically for coding in C and C++ for embedded application.
And the MISRA-C home page: http://www.misra-c.com/
This is a loaded question because avoiding a language feature can lead you to compensate for it. For instance, if you avoid using C++ classes because "vtables" take up space, but then implement your own OOP using C approaches which involve static structures containing function pointers, you've brought back vtables.
Some features can have overhead even if you don't use them, like RTTI (run time type info) and exception handling. Your compiler may have a way to turn these off.
Not using the C++ standard library and especially templates (STL) is another way to save space, provided you don't supplant these with something else that is bloated, and you actually have a way to remove the library (not have it on the target).
Be careful with how you use templates. Whenever possible, design template classes so that they are very light-weight inline functions, using an implementation that isn't a template (and so isn't expanded multiple times).
A generic container or what have you can use
void * type approaches and be wrapped by a thin template that provides the type safety. Most of the functions in the wrapper are inlines that perform casting that disappears in the code.
I think the language committee did actually take an official stance regarding subsets once -- it was strongly against them.
Other experts feel that there is some value to defining a subset for resource-constrained environments, but the subset language calling itself "Embedded C++" is useless.
The only provision that the official language makes for embedded use is the "hosted"-vs-"freestanding" implementation rules, which allow an embedded system to not support a "main()" function and its associated processing of command-line arguments.
C++ usage in most embedded systems designs of the past was to be avoided. In particular, the parts of C++ that create classes and using inheritance, operator and method overloading and use of lots of complex pointer structures for accessing data and/or code. There are two reasons for this, one being the typically limited RAM available in embedded microcontrollers. The second issue is one of basic raw performance if the CPU is handling the advanced features of C++.
There are still plenty of embedded applications, even today, that use the cheaper variety of microcontrollers where the same is true. The trend in the recent decades to have microcontrollers with all their FLASH (ROM) and RAM on board a single chip strengthens this argument even more.
In these more modern times there are now many embedded solutions that are using quite high performance processors that are SOC type components. These often support connection of large amounts of high bandwidth DDR dynamic RAM type memory for program execution and data store. These parts also support high density serial FLASH parts for loading the program code into the DDR RAM for execution even including an operating system like an embedded Linux or RTOS. These embedded systems have performance and memory resources that rival the typical PC performance of former times. Usage of C++ features in such a system is certainly feasible. That said there is a whole separate subject of embedded system predictable performance and reliability that comes into play where the complexity of an embedded Linux and/or the overloaded data structures of a C++ implementation come into question and in many instances are to be avoided.