Why is "partial RVO" not performed?

Let's consider what happens if RVO is done for f, meaning it is constructed directly in the return value. If first==true and f gets returned, great, no copy is needed. But if first==false then s gets returned instead, so the program will copy construct s over the top of f before the destructor for f has run. Then after that, the destructor for f will run, and now the return value is an invalid object that has already been destroyed!

If RVO is done for s instead the same argument applies, except that now the problem happens when first==true.

Whichever one you choose, you avoid a copy in 50% of cases and get undefined behaviour in the other 50% of cases! That's not a desirable optimization!

In order to make this work the order of destruction of the local variables would have to be altered so that f is destroyed before copying s into that memory location (or vice versa), and that's a very risky thing to mess with. The order of destruction is a fundamental property of C++ that should not be fiddled with, or you'll break RAII and who knows how many other assumptions.