如何遍历 std::variant 的类型?
How to iterate over the types of std::variant?
我有一些变体using V = std::variant<A, B, C>
和一个带有原型V parse(const json&)
的函数。该函数应尝试解析所有类型(例如A
,B
,然后C
)直到第一次成功(它应该隐式地这样做,因为时间上会有很多类型)。
如何实现这种东西?
我们可能会以某种方式使用std::variant_size
。
这是接近我需要的东西。
我的解决方案是显式列出所有类型的解析器。
V parse(const json& i_j)
{
using Parser = std::function<MaybeV(const json&)>;
static const auto ps = std::vector<Parser>{
[](const auto& j)->MaybeV{return j.get<std::optional<A>>;},
[](const auto& j)->MaybeV{return j.get<std::optional<B>>;},
[](const auto& j)->MaybeV{return j.get<std::optional<C>>;}
};
for (const auto& p : ps)
if (auto opt_result = p(i_j))
return std::move(*opt_result);
throw ParseError("Can't parse");
}
然而,它肯定会被简化,因为 lambda 仅在类型上不同,我实际需要的是迭代std::variant
的类型。
您希望编译时整数从 0 到变体的大小减 1,并且可能提前退出迭代它们。
有很多方法可以获取编译时整数。 我最喜欢的两个是生成整数常量的元组,或者使用整数常量的参数包调用延续。
以整数常量元组版本为例,您可以使用"每个元组"依次访问每个元组。
template<std::size_t I>
using index_t = std::integral_constant<std::size_t, I>;
template<std::size_t I>
constexpr index_t<I> index{};
template<std::size_t...Is>
constexpr std::tuple< index_t<Is>... > make_indexes(std::index_sequence<Is...>){
return std::make_tuple(index<Is>...);
}
template<std::size_t N>
constexpr auto indexing_tuple = make_indexes(std::make_index_sequence<N>{});
从变体大小你称之为。 由此你称之为tuple_foreach。
如果分析成功且尚未分析,则tuple_foreach将替换可选的返回值。
V parse(const json& j)
{
auto indexes = indexing_tuple<tuple_size_v<V>>;
std::optional<V> retval;
tuple_foreach(indexes, [&](auto I){ // I is compile time integer
if(retval) return;
auto p = j.get<tuple_alternative_t<I>>();
if(p) retval.emplace(std::move(*p));
});
if(!retval) throw ParseError("Can't parse");
return std::move(*retval);
}
tuple_foreach
可以在互联网上找到,但为了完整起见:
template<std::size_t...Is, class T, class F>
auto tuple_foreach( std::index_sequence<Is...>, T&& tup, F&& f ) {
( f( std::get<Is>( std::forward<T>(tup) ) ), ... );
}
template<class T, class F>
auto tuple_foreach( T&& tup, F&& f ) {
auto indexes = std::make_index_sequence< std::tuple_size_v< std::decay_t<T> > >{};
return tuple_foreach( indexes, std::forward<T>(tup), std::forward<F>(f) );
}
这应该在 C++17 中做到这一点。
类型可以从0
递归处理到std::variant_size_v
(独占),if-constexpr
限制模板实例化:
#include <variant>
#include <optional>
#include <cstddef>
#include <utility>
using V = std::variant<A, B, C>;
template <std::size_t I = 0>
V parse(const json& j)
{
if constexpr (I < std::variant_size_v<V>)
{
auto result = j.get<std::optional<std::variant_alternative_t<I, V>>>();
return result ? std::move(*result) : parse<I + 1>(j);
}
throw ParseError("Can't parse");
}
演示
相关文章:
- 更改其类型后,丢失对 std::variant 对象的引用
- C++如何乘以包含 std::variant 元素的向量的迭代器?正在执行迭代器类型的转换?
- 有没有办法避免为 std::variant 类成员中的所有类型编写构造函数?
- 在 std::variant 中按类型获取索引
- C++获取std::variant当前帮助的类型的std::typeindex
- 如何检查 std::variant 是否可以容纳某种类型
- 如何访问从 COM 对象返回的 VARIANT 数据类型中的安全数组C++?
- 具有 std::map 和 std::variant 的不完整类型
- std::bind to a std::variant 包含多个 std::函数类型
- 如何遍历 std::variant 的类型?
- 如何将当前替代类型的 std::variant 传递给可调用对象?
- 如何构造一个 std::variant 类型对象,其自身 Templated 和构造函数转发参数
- 将 std::variant 转换为另一个具有类型子集的 std::variant
- 为什么 std::variant 不能容纳数组对象类型,而联合可以?
- 返回类型 std::optional<std::variant<...>>
- boost::variant 当 bool 显示为可能的类型时,会给出错误的结果
- std::variant 存储多个字符串类型并区分它们
- 为什么不允许 std::variant 与其替代类型之一相等?
- 将 std::variant 转换为另一个具有超类型集的 std::variant
- 为什么我的结构不能有 boost::variant 类型的成员,但可以有 vector<boost::variant> 类型的成员?