вектор типов std::variant в std::tuple

Я работаю над вектором типов std::variant. Есть ли способ преобразовать его в std::tuple значений, содержащихся в данных std::variants ?

typedef std::variant<type1, type2,...> a_union;
std::vector<a_union> vec;

Например, я хотел бы иметь кортеж вроде:

std::tuple<typeX, typeY, typeX,...>

Где членами являются типы, содержащиеся в последовательных вариантах в векторе.


person bring112    schedule 22.09.2020    source источник
comment
Знаете ли вы заранее (во время компиляции), какие типы будет содержать ваш вектор?   -  person François Andrieux    schedule 22.09.2020
comment
Обратите внимание, что std::tuple — это объект времени компиляции, а std::vector — объект времени выполнения. Если вы не знаете, какой тип будут хранить варианты во время компиляции или сколько объектов у вас будет в векторе во время компиляции, создание кортежа практически невозможно.   -  person NathanOliver    schedule 22.09.2020
comment
вектор имеет динамический размер. Каждый вариант внутри может иметь любой тип, так что же такое сеанс для создания кортежа (который является статическим типом) для этого? IMO это XY проблема, и вы должны объяснить, зачем вам нужна эта странная вещь, чего на самом деле вы пытаетесь достичь (а не как вы думаете, вы можете достичь этого)?   -  person Marek R    schedule 22.09.2020
comment
Это возможно только в том случае, если вы точно знаете, что vector::size() содержит то же количество записей, что и тип tuple<...>, и если вы точно знаете, какие типы из варианта будут содержать каждый элемент. Хотя это можно сделать, вряд ли это будет стабильным или хорошим решением проблемы, которую вы пытаетесь решить.   -  person Human-Compiler    schedule 22.09.2020


Ответы (2)


Это может быть решением для вас, оно использует необязательный параметр и возвращает nullopt, если вектор имеет неверные значения.

#include <optional>
#include <tuple>
#include <utility>
#include <variant>
#include <vector>
 
template<typename ... T, size_t ... Index>
std::optional<std::tuple<T...>> to_tuple(const std::vector<std::variant<T...>> & vec,
                                         std::index_sequence<Index...>)
{
    if (sizeof ... (T) != vec.size())
        return std::nullopt;
        
    if (not (...&& std::holds_alternative<T>(vec[Index])))
        return std::nullopt;
    return std::tuple<T...>(std::get<T>(vec[Index])...);
}

template<typename ... T>
std::optional<std::tuple<T...>> to_tuple(const std::vector<std::variant<T...>>& vec)
{
    return to_tuple(vec, std::index_sequence_for<T...>{});
}
person jo-art    schedule 22.09.2020

В комментариях точно указано, что это, вероятно, проблема XY - кортеж требует информации времени компиляции о типах данных в каждом индексе, которой нет у вектора вариантов.

Но если вы хотите предоставить эту информацию на сайте вызова, довольно просто использовать расширение пакета параметров для сопоставления списка типов со строкой вызовов std::get<>.

Вы можете предоставить этот список типов, предполагая, что порядок типов в варианте является желаемым типом варианта в каждом индексе, как это делает ответ jo-art. Вот способ сделать это, просто предоставив список типов, которые вы ожидаете, что кортеж будет содержать вектор, если они разные:

template<typename... Ts, typename Container, std::size_t... Is>
auto vector_to_tuple_impl(Container&& items, std::index_sequence<Is...>)
{
    return std::make_tuple(std::get<Ts>(items[Is])...);
}

template <typename... Ts, typename Container>
std::tuple<Ts...> vector_to_tuple(Container&& items)
{
    return vector_to_tuple_impl<Ts...>(items, std::index_sequence_for<Ts...>{});
}

(здесь нет обработки ошибок, он выдаст std::bad_variant_access, если вы ошибетесь с типами, и неопределенное поведение, если вы извлечете больше элементов, чем существует)

Это та же базовая стратегия: используйте std::index_sequence_for, чтобы превратить пакет параметров в расширяемый пакет параметров индексов контейнера (0, 1, 2 и т. д.). Пакет целочисленной последовательности и пакет типов расширяются вместе, чтобы получить элемент по каждому индексу, и вызывают std::get для извлечения значения.

Применение:

    using SimpleVariant = std::variant<std::string_view, int>;
    std::vector<SimpleVariant> some_list { "hello", 42, "goodbye" };
    auto as_tuple = vector_to_tuple<std::string_view, int, std::string_view>(some_list);

доказательство концепции: https://godbolt.org/z/cGEW5s

person parktomatomi    schedule 22.09.2020