I'm not saying it's perfect but it's better than dealing with C++ exceptions. At least with error codes you can manually do some printing to find out what went wrong. With C++ exceptions you don't even get a line number or a stack trace.
You also don’t have to make the anemic constructor public.
Later you get into another whole debate about movable types and what T should be once it's moved from (for example if you want to turn that std::optional<T> into a std::shared_ptr<T>), if it only has non-trivial constructors. An idea that just popped into my head would be to have a T constructor from std:optional<T>&&, which moves from it and resets the optional. But then it's not a real move constructor. With move constructors, a lot of times you just have to accept they'll have to leave the other object in some sort of empty or invalid state.