我已经修改了this answer中的某些代码,以处理目标变体是源变体的子集的情况,如下所示:
template <class... Args>
struct variant_cast_proxy
{
std::variant<Args...> v;
template <class... ToArgs>
operator std::variant<ToArgs...>() const
{
return std::visit(
[](auto&& arg) -> std::variant<ToArgs...> {
if constexpr (std::is_convertible_v<decltype(arg), std::variant<ToArgs...>>)
return arg;
else
throw std::runtime_error("bad variant cast");
},
v
);
}
};
template <class... Args>
auto variant_cast(const std::variant<Args...>& v) -> variant_cast_proxy<Args...>
{
return { v };
}
struct A {};
struct B {};
struct C {};
struct D {};
struct E {};
struct F {};
int main() {
std::variant<A, B, C, D> v1 = B();
std::variant<B,C> v2;
v2 = variant_cast(v1);
}
上面的方法有效,但是我希望它能处理在编译时检测到错误转换的情况。上面的代码在运行时处理了所有错误的转换,但是运行时和编译时错误都是可能的。如果v持有类型C的值,则在运行时将类型为std::variant<A,B,C>
的v转换为std::variant<A,B>
应该会失败,但是例如
std::variant<A, B, C, D> v1 = B();
std::variant<E,F> v2;
v2 = variant_cast(v1)
甚至不应该编译。
我相信可以通过std :: enable_if来完成,但不确定确切如何需要测试可变参数包的集合包含,我不知道该怎么做。
static_assert
来检查是否有任何可能持有的变体是可转换的:static_assert((std::is_convertible_v<Args, std::variant<ToArgs...>> || ...),
"No possible variant that could be converted exists");
或者,如果需要SFINAE,则可以在模板参数中进行:
// extracted into helper function template <class... ToArgs> static constexpr bool is_convertible() noexcept { return (std::is_convertible_v<Args, std::variant<ToArgs...>> || ...); } template<class... ToArgs, std::enable_if_t<is_convertible<ToArgs...>(), int> = 0> operator std::variant<ToArgs...>() const { // ... }
convertible是一个错误的问题...除非您真的希望能够像variant<int, long>
一样转换为variant<string, double>
。我认为更好的检查方法是将源variant
中的每种类型都显示在目标variant
中。
template <class... Args>
struct variant_cast_proxy
{
std::variant<Args...> v;
template <class... ToArgs,
std::enable_if_t<
// every type is present
(mp_contains<variant_cast_proxy, ToArgs>::value && ...)
// and they're distinct
&& mp_is_set<mp_list<ToArgs...>>::value
, int> = 0>
operator std::variant<ToArgs...>() const
{
return std::visit([&](auto const& arg){
return std::variant<ToArgs...>(arg);
}, v);
}
};