将变体强制转换为超集变体或子集变体



我根据这个答案改编了一些代码,以处理目标变体是源变体的子集的情况,如下所示:

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
{
// ...
}

我认为可转换是个错误的问题。。。除非您真的希望能够像CCD_ 4到CCD_。我认为更好的检查是源variant中的每个类型都出现在目标variant中。

为此,您可以使用Boost.Mp11来简化此检查:

template <class... Args>
struct variant_cast_proxy
{
std::variant<Args...> v;
template <class... ToArgs,
class V = std::variant<ToArgs...>,
std::enable_if_t<
// every type in the source variant is present in the destination
(mp_contains<V, Args>::value && ...)
// and the destination id all distinct
&& mp_is_set<V>::value
, int> = 0>
operator std::variant<ToArgs...>() const
{
return std::visit([&](auto const& arg){ return V(arg); }, v);
}
};

最新更新