【问题标题】:Casting a variant to super set variant or a subset variant将变体转换为超集变体或子集变体
【发布时间】:2020-04-05 17:34:47
【问题描述】:

我已经修改了来自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&lt;A,B,C&gt; 类型的 v 转换为 std::variant&lt;A,B&gt; 应该在运行时失败,但是例如

std::variant<A, B, C, D> v1 = B();
std::variant<E,F> v2;
v2 = variant_cast(v1)

甚至不应该编译。

我相信这可以通过 std::enable_if 完成,但我不确定它似乎需要测试可变参数包的集合包含,我不知道该怎么做。

【问题讨论】:

  • 有趣...我也使用链接帖子中的代码,但是当我编译您的示例时,出现以下编译器错误:error: could not convert ‘arg’ from ‘const A’ to ‘std::variant&lt;E, F&gt;’

标签: c++ c++17 std-variant


【解决方案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
    {
        // ...
    }

【讨论】:

  • 谢谢,我试图找出 SFINAE 但 static_assert 好多了。
  • 你想要&amp;&amp;,而不是||
  • @Barry 那么您将无法转换为子集(例如问题中给出的std::variant&lt;A,B,C&gt;std::variant&lt;A,B&gt; 示例)
【解决方案2】:

我认为 convertible 是一个错误的问题...除非您真的希望能够像 variant&lt;int, long&gt; 一样转换为 variant&lt;string, double&gt;。我认为更好的检查是源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);
    }
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-05-15
    • 2018-06-02
    • 1970-01-01
    • 1970-01-01
    • 2019-12-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多