Seen some other questions about this but they so not seem to be answered correctly or there is no answer at all.
I have a instance of a std::variant
and I want to create another instance with a sub set of the types in the first one as I know the original is not that type. For exmaple...
std::varaint<int, const char*, bool> v1 = false;
std::varaint<int, bool> v2 = cast_variant<int, bool>(v1);
As you can see I can guarantee v1
does have a value that is in the list of types for v2
. Just wondering how you would implement this or can you?
CodePudding user response:
you can use std::visit
to get the item, then create new std::variant
from it
template <typename...Ts, typename...Us>
std::variant<Ts...> cast_variant(const std::variant<Us...>& variant){
return std::visit(
[&](auto&& v)->std::variant<Ts...>{
// or you can check if type of v inside Ts...
if constexpr(requires {std::variant<Ts...>{v};})
return v;
else
throw std::bad_variant_access();
}
,variant
);
}
CodePudding user response:
@appleapple already provided an answer. However,
if constexpr(requires {std::variant<Ts...>{v};})
This can easily triggers implicit conversion, so the below code will work without throwing any exceptions:
std::varaint<int, const char*, bool> var1 = false;
auto var2 = cast_variant<int>(var1); // convert `false` to int
Instead, consider explicitly checking if current type is included in new types:
template<typename ... newTypes, typename ... oldTypes>
auto cast_variant(const std::variant<oldTypes...>& var) {
return std::visit([]<typename T>(T && arg) -> std::variant<newTypes...> {
if constexpr (std::disjunction_v<std::is_same<std::decay_t<T>, newTypes>...>) {
return arg;
}
else {
throw std::bad_variant_access();
}
}, var);
}