我正在研究 std::variant 类型的向量。有没有办法将它转换为给定 std::variants 所持有的值的 std::tuple ?
typedef std::variant<type1, type2,...> a_union;
std::vector<a_union> vec;
例如,我想要像这样的元组:
std::tuple<typeX, typeY, typeX,...>
其中成员是向量中连续变体所持有的类型。
我正在研究 std::variant 类型的向量。有没有办法将它转换为给定 std::variants 所持有的值的 std::tuple ?
typedef std::variant<type1, type2,...> a_union;
std::vector<a_union> vec;
例如,我想要像这样的元组:
std::tuple<typeX, typeY, typeX,...>
其中成员是向量中连续变体所持有的类型。
这可能是您的解决方案,它使用可选的,如果向量的值不正确,则返回 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...>{});
}
评论准确地指出这可能是一个 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);