Whether or not the moves do anything or not is entirely dependent on the type T here, not optional, and is therefore orthogonal to whether or not you use optional. Ie, optional did not introduce any new concerns, and therefore doesn't warrant any particular scrutiny or concern.
Also, the fact that std::optional::value() apparently returns a reference, and thus `return opt.value()` is always forced to be a copy (I'm writing this based on another comment, I can't claim to fully understand C++'s extraordinarily complicated value types and their respective limitations) seems like another problem that's somewhat specific to std::optional itself.
I don't think I'd agree with a characterization that `value_or` is a footgun? It behaves basically identically to similar features in every other language (like Java's Optional#orElse). If it somehow changed parameter evaluation such that it was deferred then that would be a footgun. But it's of course not doing that.
Of course, even in Java this method is often inferior - it only really makes sense in languages like Haskell or if replaced by a macro of some kind, which would guarantee that the unnecessary value is neither constructed nor copied. Otherwise, an if/else or ? should generally be preferred, especially in a performance-focused language like C++.
value_or's parameter type is U&&, so no there's no additional copy there. There's only the explicitly visible default constructor call.
But you did get me to take a second look at the post, and yeah the author's statement of "Finally, we copy construct an instance of A when the call to value_or terminates." is just incorrect. Try it out yourself: https://godbolt.org/z/hWqnrfGKE
If you replace the `.value_or(A())` with `.value()` there's no reduction in copies, only a single reduction in the constructed count which of course makes sense since the `A()` parameter isn't there.
The way you would optimize this to eliminate copies would be to do things like construct the optional in-place and take the parameter by const& instead, such as:
A f(const std::optional<A>& z) {
return z.value_or(A());
}
A g() {
std::optional<A> z(std::in_place, "message");
return f(z);
}
`std::in_place` being definitely a classic C++ "quirk" here and usually unlikely to be worth it but hey if you want to play "minimize the copy count" golf there you go. The value_or isn't actually adding any copies, though.Just z.value() gives you a reference, which, when returned will yield a copy. So no RVO in this case and the move is the next best thing.
"Most of these problems can be solved with moves" - It may be that the problem of copies is solved by using moves (i.e. this is a solved problem at the compiler level). But the problem of needing to be careful is not solved by the advice to use moves (i.e. it's trickier at the human level). It's so easy to forget to use move and you would get no warning about it.
It doesn't help that the rules are very complicated. For example, if you were returning z rather than z.value() from your function (with correspondingly different return type), the usual advice is to return z rather than return std::move(z) because it's already implicitly an rvalue in that situation and using move() prevents NRVO. But... is that still true for z.value()? I don't think so but it's not obvious (IMO). Or, if you return std::move(x) where x is a const type, it will copy, which is fair enough as that's all it can do but it ought to warn you (or not compile IMO). It's not always obvious is something is const e.g. if you have a template function, x is of deduced type T (passed by copy) is const part of the deduced type? I'm not sure.