I often need to use optional type for functions:
std::optional<int32_t> get(const std::string& field) { auto it = map.find(field); if (it != map.end()) return it->second; return {}; }
Is there a way to return optional value in one line? e.g. this:
std::optional<int32_t> get(const std::string& field) { auto it = map.find(field); return it != map.end() ? it->second : {}; }
results in the error
error: expected primary-expression before '{' token return it != map.end() ? it->second : {}; ^
The class template std::optional manages an optional contained value, i.e. a value that may or may not be present[1]. It is a great alternative for std::unique_ptr , or raw pointer, when used solely to express that a value is indeed optional.
Any instance of optional<T> at any given point in time either contains a value or does not contain a value. If an optional<T> contains a value, the value is guaranteed to be allocated as part of the optional object footprint, i.e. no dynamic memory allocation ever takes place.
C++17 introduced std::optional<T> which lets you augment the values of a type T with a bonus value known as std::nullopt which semantically represents the absence of a value. A std::optional which holds the value std::nullopt is known as empty.
What's more, std::optional doesn't need to allocate any memory on the free store. std::optional is a part of C++ vocabulary types along with std::any , std::variant and std::string_view .
You can explicitly wrap the some-value return into an std::optional
, and fall back on the constexpr
std::nullopt
for the no-value return.
std::nullopt
:
std::nullopt
is a constant of typestd::nullopt_t
that is used to indicate optional type with uninitialized state....
std::nullopt_t
:
std::nullopt_t
is an empty class type used to indicate optional type with uninitialized state. In particular,std::optional
has a constructor withnullopt_t
as a single argument, which creates an optional that does not contain a value.
With this approach, the true clause of the ternary operator call explicitly returns an std::optional
with a some-value, so the compiler can deduce the template parameter/wrapped type (in this example: int32_t
) from the type of the supplied wrapped value, meaning you needn't specify it explicitly.
Applied to your example:
return it != map.end() ? std::optional(it->second) : std::nullopt; // alternatively return it != map.end() ? std::make_optional(it->second) : std::nullopt;
return it != map.end() ? it->second : std::optional<int32_t>{};
should do the trick.
The compiler must deduce the result type of the ternary expression from the last two operands, but there is no way it can deduce std::optional<int32_t>
from int32_t
and {}
.
int32_t
and std::optional<int32_t>
on the other hand do have the desired common type std::optional<int32_t>
.
Related fun fact: You can avoid repeating the type with auto return type deduction:
auto get(const std::string& field) { auto it = map.find(field); return it != map.end() ? it->second : std::optional<int32_t>{}; }
Depending on preference, you can of course also infer the template argument for the std::optional
from it->second
with decltype
to further reduce repetition.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With