IMHO this sort of named arguments "easter egg" is fine for big "option bag structs" passed to functions that are not performance critical (or for small struct <= 16 bytes). For other cases, regular args are better.
OTH it seems like passing normal args also spills over into the stack very soon (only 4 registers used?):
https://www.godbolt.org/z/Mz9MTG
The main difference seems to be that normals arguments "spill over", while passing structs by value puts everything on the stack once the threshold size is passed.
It's more complicated than that - floats/doubles and structs of them are put in SSE registers instead. Mixed structs are packed into integer registers (!) if they are small enough.
And with C++ classes with non-trivial destructor/etc. are always passed on the stack.
In any case you don't get to actually see the passing at the callsite because the result `45` is inlined into `main()`. :)
However, if you use an lvalue expression then yes, the copy constructor is invoked (in `bar` here), and you can avoid that by using an xvalue expression (in `baz`, using `std::move`): https://godbolt.org/z/MnKnWM
Basically it doesn't depend on being passed through a struct as much as on the parameter itself. You'd see the same behavior if the `C` struct was passed as a regular parameter.
> If the size of an object is larger than eight eightbytes, or it contains unaligned fields, it has class MEMORY
> [...]
> If the size of the aggregate exceeds a single eightbyte, each is classified separately. Each eightbyte gets initialized to class NO_CLASS.
So that only happens if the whole structure is bigger than 64 bytes. Otherwise everything gets passed in registers like normal.
> If the size of the aggregate exceeds two eightbytes and the first eight-byte isn’t SSE or any other eightbyte isn’t SSEUP, the whole argumentis passed in memory.
However in my experience there are human factors problems which I posted in another comment.
Structs with more than one field don't fit into a register.