Приведение варианта к варианту супернабора или варианту поднабора - PullRequest
1 голос
/ 05 апреля 2020

Я адаптировал некоторый код из этого ответа для обработки случая, когда целевой вариант является подмножеством исходного варианта, следующим образом:

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 типа std::variant<A,B,C> к std::variant<A,B> должно завершиться неудачно во время выполнения, если v содержит значение типа C, но, например,

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

даже не должно компилироваться.

I Я полагаю, что это можно сделать с помощью std :: enable_if, но я не уверен, что именно так, как кажется, потребовалось бы тестирование на предмет наличия набора пакетов variadi c, что я понятия не имею, как это сделать.

Ответы [ 2 ]

1 голос
/ 05 апреля 2020

Я думаю, конвертируемый - неправильный вопрос ... если только вы действительно не хотите иметь возможность разыграть как variant<int, long> до variant<string, double>. Я думаю, что лучшей проверкой будет то, что каждый тип в источнике 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);
    }
};
1 голос
/ 05 апреля 2020

Вы можете добавить 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
    {
        // ...
    }
...