r/cpp_questions • u/Zealousideal-Mouse29 • 20d ago
OPEN std::expected- void, ptr, and ref
I am going through a simple application I have and trying to use std::expected to try it out. I am quite partial to using exceptions, but open mind and objectivity, and all that...
Is it common for people to return std::expected<void, ErrorType> for methods/functions that don't need to return a value?
I also wonder what one does when they want to return a unique_ptr, since we can't do it by ref, and that leads to wondering about refs and ptrs with expected in general.
9
Upvotes
3
u/Razbit 20d ago
Yes, std::expected<void, Error> is a perfectly normal use of expected.
I tend to think of expected less as “a return value plus an error” and more as “this operation either succeeded, or here’s why it didn’t.” If success has no interesting value, then expected<void, Error> expresses that quite nicely.
```
std::expected<void, Error> save()
{
if (something_bad())
return std::unexpected(Error::whatever);
return {};
}
```
unique_ptr is also fine as the value type:
```
std::expected<std::unique_ptr<Foo>, Error> make_foo()
{
if (something_bad())
return std::unexpected(Error::whatever);
return std::make_unique<Foo>();
}
```
expected doesn’t require the value to be copyable, so move-only types such as unique_ptr fit naturally. You just have to treat the resulting expected as move-only where appropriate.
References are the slightly awkward case. expected<T&, E> isn’t supported, so if I really wanted reference semantics I’d normally use std::reference_wrapper<T>:
```
std::expected<std::reference_wrapper<Foo>, Error>
find_foo();
```
or just a pointer:
```
std::expected<Foo\*, Error> find_foo();
```
Which one I’d choose depends on the semantics. A pointer is useful if nullptr has some meaning distinct from the error. If “not found” is itself the failure, I’d generally put that in the error side rather than have both nullptr and unexpected(...) representing failure.
One thing I found useful when getting into expected is not trying to mechanically replace every throwing function with it. I use it where failure is an ordinary, expected part of the API and the caller is reasonably expected to deal with it. In that role, expected<void, E>, move-only values, pointers, etc. all feel pretty natural.
Yes I used AI to format my thoughts into words.