If I have an std::optional<T>
then how can I transparently cast it to a std::optional<U>
given that T
and U
are compatible types and preserving std::nullopt
?
That is, if the initial std::optional<T>
was a std::nullopt
then the resulting std::optional<U>
shall be the same. Otherwise the requested cast shall be performed (static/dynamic/reinterpret...).
struct B { };
struct C: public B { };
std::optional<C *> c;
std::optional<B *> b;
b = c; // works - a C can be implicitely up-casted to a B
//c = b; // doesn't work, explicit down-cast (involving knowledge) required
I know there is std::optional::transform
, and I can work-around it by formulating if/else
myself, of course.
Just asking, I guess I'm overlooking an elegant way.
std::optional<T>
and std::optional<U>
are completely separate types in general, and so converting one to the other isn't normally possible.
It does work when U
is constructible from T
because std::optional
has a converting constructor (see (4)) for this case.
When this constructor cannot be used, you need a std::optional
counterpart of std::static_pointer_cast
et al.
Nothing like that exists in the standard library, but you could use std::optional::transform
directly, or implement such a cast yourself:
std::optional<void*> v;
// if v is empty, produces an empty std::optional<int*>,
// otherwise applies the transformation, which is a static_cast
std::optional<int*> i = v.transform([](void* p) {
return static_cast<int*>(p);
});
If you do this a lot, here's how you can write a static_optional_cast
, similar to std::static_pointer_cast
:
template <typename To, typename From>
// might want to use a proper concept here
requires requires (const From& from) { static_cast<To>(from); }
std::optional<To> static_optional_cast(const std::optional<From>& from) {
// Could also use transform here, but for the sake of demonstration,
// let's do it "manually":
if (!from) return {};
return static_cast<To>(*from);
}
// ...
std::optional<void*> v;
std::optional<int*> j = static_optional_cast<int*>(v);