c++ - 如何展平异构列表(又名元组的元组......)
问题描述
我正在尝试使用 C++17 折叠表达式和 C++14 索引技巧来展平由元组和非元组组成的任意输入。
预期结果至少应符合以下要求:
constexpr auto bare = 42;
constexpr auto single = std::tuple{bare};
constexpr auto nested_simple = std::tuple{single};
constexpr auto multiple = std::tuple{bare, bare};
constexpr auto nested_multiple = std::tuple{multiple};
constexpr auto multiply_nested = std::tuple{multiple, multiple};
static_assert(flatten(bare) == bare);
static_assert(flatten(single) == bare);
static_assert(flatten(nested_simple) == bare);
static_assert(flatten(multiple) == multiple);
static_assert(flatten(nested_multiple) == multiple);
static_assert(flatten(multiply_nested) == std::tuple{bare, bare, bare, bare});
除了最后一种情况,我有相对简单的代码来处理所有情况:
template<typename T>
constexpr decltype(auto) flatten(T&& t)
{
return std::forward<T>(t);
}
template<typename T>
constexpr decltype(auto) flatten(std::tuple<T> t)
{
return std::get<0>(t);
}
template<typename... Ts>
constexpr decltype(auto) flatten_multi(Ts&&... ts)
{
return std::make_tuple(flatten(ts)...);
}
template<typename... Ts, std::size_t... Indices>
constexpr decltype(auto) flatten_impl(std::tuple<Ts...> ts, const std::index_sequence<Indices...>&)
{
return flatten_multi(std::get<Indices>(ts)...);
}
template<typename... Ts>
constexpr decltype(auto) flatten(std::tuple<Ts...> ts)
{
return flatten_impl(ts, std::make_index_sequence<sizeof...(Ts)>());
}
现场演示在这里。显然,它不能很好地处理多重嵌套项。
multiply_nested
处理我还没有找到的情况的更高级的形式。我尝试申请operator>>
能够使用折叠表达式,但无法获得任何可以编译的东西。我的最后一次尝试可以在这里找到。核心思想是operator>>
在折叠表达式中使用 2 到 2 的元素组合,每次展开前一个结果。
在我看来,我应该能够使用类似的东西std::tuple_cat
,但由于我无法完全破译的原因,它对我大喊大叫。
所以我的问题是:我错过了什么?如何解开任意深度任意嵌套的类元组输入?
解决方案
namespace flattenns {
struct flat_t {};
template<std::size_t... Is, class...As>
constexpr auto flatten( std::index_sequence<Is...>, flat_t, std::tuple<As...> as ) {
return std::tuple_cat( flatten(flat_t{}, std::get<Is>(as))... );
}
template<class...As, class...Ts>
constexpr auto flatten( flat_t, std::tuple<As...> as ) {
return flatten( std::make_index_sequence<sizeof...(As)>{}, flat_t{}, as );
}
template<class T>
constexpr std::tuple<T> flatten( flat_t, T t ) { return {t}; }
template<class...Ts>
constexpr auto flatten( flat_t, Ts... ts ) {
return std::tuple_cat( flatten(flat_t{}, ts)... );
}
constexpr std::tuple<> flatten( flat_t ) { return {}; }
}
template<class...Ts>
constexpr auto sane_flatten( Ts...ts ) {
return flattenns::flatten(flattenns::flat_t{}, ts...);
}
// to take std::tuple<int>(7) -> 7
namespace insanens {
template<class...Ts>
constexpr auto unpack_single( std::tuple<Ts...> t ) {return t;}
template<class T>
constexpr auto unpack_single( std::tuple<T> t ) {return std::get<0>(t);}
}
template<class...Ts>
constexpr auto insane_flatten( Ts...ts ) {
return insanens::unpack_single( sane_flatten(ts...) );
}
template<class...Ts>
constexpr auto flatten( Ts...ts ) {
return insane_flatten(ts...);
}
如上所述,flatten( std::tuple<int>(7) )
不应该是7。那是精神错乱。
但如您所愿,我将其添加为后处理步骤。
您的操作在其他方面相对健全。您正在递归地[[x],[y]]
申请[x,y]
. 最后的拆箱是不理智的。通过拆分,代码变得简单,这也证明了它为什么是疯狂的。
如果您想知道,flat_t
标签类型的存在是为了 (a) 从可能的参数中拆分索引序列(这可以通过使用不同的函数名称来完成)和 (b) 启用 ADL 查找,以便每个 flatten 实现都可以看到所有其他的。
推荐阅读
- java - 每当我尝试输入我的设置时,logcat 都会向我显示此错误,我不知道出了什么问题
- batch-file - 詹金斯 - cmd无法识别
- python - numpy tofile 在每一行中放置单引号
- javascript - 循环常量,让图声明......如何解决?
- python - 将列表的最后两个整数的总和附加到同一个列表
- html - 是否可以仅使用引导程序来实现此布局?
- java - 使用 JavaAssist 修改现有类时出错
- css - 如何从 GitHub 以不同的权重显示 Noto Sans?
- python - 使用多列定义的频率填充 groupby 中的缺失日期
- vue.js - 动态绑定到这个 vuejs