将变体投射到超集变体或子集变体

问题描述 投票:0回答:2

我已经修改了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来完成,但不确定确切如何需要测试可变参数包的集合包含,我不知道该怎么做。

c++ c++17 std-variant
2个回答
1
投票
您可以添加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 { // ... }


0
投票
我认为

convertible是一个错误的问题...除非您真的希望能够像variant<int, long>一样转换为variant<string, double>。我认为更好的检查方法是将源variant中的每种类型都显示在目标variant中。

为此,您可以使用Boost.Mp11轻松进行此检查:

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); } };

© www.soinside.com 2019 - 2024. All rights reserved.