C translates directly to ASM in many cases. It just makes managing offsets and other stuff easier.
C++ adds type-safety on top of that for no cost. It's great when your compiler tells you that there is no operator =|(PORTD, PINA). Did you mean |=(PORTD,PIND) or =|(PORTA,PINA).
But usually much worse ASM than what a human would write on such CPUs, because the C compiler is still restricted by artificial high-level concepts like calling conventions, and it needs to wrestle with instruction sets that are not very compiler-friendly and tiny non-orthogonal register sets. C++ just adds a whole level of code obfuscation on top, so it's harder to tweak what code the compiler actually generates.
If you really want that in C, you can either use functions and wrap everything in (incompatible but internally identical) structs, or use Sparse and annotate those integer types to be incompatible. Not that you must prefer that to C++ (even if I do), just to note that you can make do with C if you want to.
C++ adds type-safety on top of that for no cost. It's great when your compiler tells you that there is no operator =|(PORTD, PINA). Did you mean |=(PORTD,PIND) or =|(PORTA,PINA).