如何将std :: variant的元素复制到另一个variant-type的变量 [英] How to copy an element of std::variant to a variable of another variant-type

查看:116
本文介绍了如何将std :: variant的元素复制到另一个variant-type的变量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是对此答案的跟进. 假设我们有两种类型的 std:variant ,其成员类型部分相同.例如,如果我们有

This is a followup on this answer. Assume we have two types of std:variant with partly the same member types. For instance if we have

struct Monday {};
struct Tuesday {};
/* ... etc. */
using WeekDay= std::variant<Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday>;
using Working_Day= std::variant<Monday, Tuesday, Wednesday, Thursday, Friday>;

Working_DayWeekDay的子类型.现在我们如何将一种类型的变量复制到另一种类型的变量?如果源的所有类型成员都是目标的类型成员,则可以将转换函数定义为

Working_Day is a sub-type of WeekDay. Now how can we copy a variable of one type to a variable of the other type? If all type members of the source are type members of the target a conversion function can be defined as

template <typename To, typename From>
To var2var( From && from )
{
    return std::visit(
        []( auto && elem ) { return To( std::forward<decltype(elem)>( elem ) ); },
        std::forward<From>( from ) );
}

它可以用作

Working_Day  d1= Tuesday{};
WeekDay      d2= var2var<WeekDay>( d1 );

以另一种方式尝试,即将WeekDay转换为Working_Day,会导致编译时错误.有什么解决办法吗?

Trying this the other way around, i.e. casting a WeekDay into a Working_Day, results in a compile time error. Is there any solution for this?

推荐答案

显然,要求是,如果目标变体中不存在该类型,则抛出异常.我们可以通过引入只能精确地转换为特定目标的新类型来做到这一点:

Apparently the requirement is that if the type isn't present in the target variant, throw an exception. We can do that by introducing a new type which is only exactly convertible to a specific target:

template <typename T>
struct Exactly {
    template <typename U, std::enable_if_t<std::is_same_v<T, U>, int> = 0>
    operator U() const;
};

然后使用它来构造或抛出:

And then use that to either construct or throw:

template <typename To, typename From>
To unsafe_variant_cast(From && from)
{
    return std::visit([](auto&& elem) -> To {
        using U = std::decay_t<decltype(elem)>;
        if constexpr (std::is_constructible_v<To, Exactly<U>>) {
            return To(std::forward<decltype(elem)>(elem));
        } else {
            throw std::runtime_error("Bad type");
        }
    }, std::forward<From>(from));
}

请注意,您需要显式提供一个返回类型,因为在特殊情况下,它会被推导为void并且访问者不会都具有相同的返回类型.

Note that you need to explicitly provide a return type because otherwise in the exceptional case, it would get deduced to void and the visitors wouldn't all have the same return type.

使用Exactly<U>而不是decltype(elem)意味着将variant<int>强制转换为variant<unsigned int>会抛出而不是成功.如果打算使它成功,则可以改用decltype(elem).

The use of Exactly<U> as opposed to just decltype(elem) means that casting a variant<int> to a variant<unsigned int> will throw instead of succeeding. If the intend is to have it succeed, you can use decltype(elem) instead.

这里的替代方法是使用

An alternative here would be to use Boost.Mp11, in which everything template metaprogramming related is a one-liner. This is also a more direct check:

template <typename To, typename From>
To unsafe_variant_cast(From && from)
{
    return std::visit([](auto&& elem) -> To {
        using U = std::decay_t<decltype(elem)>;
        if constexpr (mp_contains<To, U>::value) {
            return To(std::forward<decltype(elem)>(elem));
        } else {
            throw std::runtime_error("Bad type");
        }
    }, std::forward<From>(from));
}

这篇关于如何将std :: variant的元素复制到另一个variant-type的变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆