三角化元组
Triangularizing a tuple
我一直在尝试使用 C++ 的现代功能从 2012 年开始升级(并稍微调整(这个解决方案。
我的目标实际上比这个问题稍微简单一些;我想要这个:
triangularize_t<T0, T1, ..., TN-1, TN>
等效于此:
std::tuple<
std::tuple<>,
std::tuple<T0>,
std::tuple<T0, T1>,
...
std::tuple<T0, T1, ..., TN-1>
>
这样std::tuple_element_t<N, result>
就有N
元素。类型TN
不应出现在输出中的任何位置。
以下是我到目前为止所做的工作:
template <class _Tuple, class _Seq>
struct _tuple_head;
template <class _Tuple, size_t... _N>
struct _tuple_head<_Tuple, std::index_sequence<_N...>> {
using type = std::tuple<std::tuple_element_t<_N, _Tuple>...>;
};
template <class _Tuple, class _Seq>
struct _triangularize_impl;
template <class _Tuple, size_t... _N>
struct _triangularize_impl<_Tuple, std::index_sequence<_N...>> {
using type = std::tuple<typename _tuple_head<_Tuple, std::make_index_sequence<_N>>::type...>;
};
template <class... _Pack>
struct triangularize {
using type = _triangularize_impl<std::tuple<_Pack...>, std::index_sequence_for<_Pack...>>;
};
template <class... _Pack>
using triangularize_t = typename triangularize<_Pack...>::type;
但是,它似乎并没有像我期望的那样扩展。 使用triangularize_t<int, float>
作为测试,错误消息似乎报告std::get<0>
和1
的输出返回这些类型(由于某种原因相同(。
_triangularize_impl<tuple<std::__1::tuple<int, float> >, __make_integer_seq<integer_sequence, unsigned long, 1UL> >
_triangularize_impl<tuple<std::__1::tuple<int, float> >, __make_integer_seq<integer_sequence, unsigned long, 1UL> >
我在这里错过了什么?
也许有人可以用更简单的方式做到这一点......但是下面呢?
template <typename T, std::size_t ... Is>
auto gtt_helper (std::index_sequence<Is...>)
-> std::tuple<std::tuple_element_t<Is, T>...>;
template <typename ... Ts, std::size_t ... Is>
auto getTriTuple (std::index_sequence<Is...>)
-> std::tuple<decltype(gtt_helper<std::tuple<Ts...>>
(std::make_index_sequence<Is>{}))...>;
template <typename ... Ts>
using triTuple
= decltype(getTriTuple<Ts...>(std::index_sequence_for<Ts...>{}));
以下是完整的编译C++14示例
#include <type_traits>
#include <utility>
#include <tuple>
template <typename T, std::size_t ... Is>
auto gtt_helper (std::index_sequence<Is...>)
-> std::tuple<std::tuple_element_t<Is, T>...>;
template <typename ... Ts, std::size_t ... Is>
auto getTriTuple (std::index_sequence<Is...>)
-> std::tuple<decltype(gtt_helper<std::tuple<Ts...>>
(std::make_index_sequence<Is>{}))...>;
template <typename ... Ts>
using triTuple
= decltype(getTriTuple<Ts...>(std::index_sequence_for<Ts...>{}));
int main ()
{
using T0 = triTuple<char, int, long, long long>;
using T1 = std::tuple<std::tuple<>,
std::tuple<char>,
std::tuple<char, int>,
std::tuple<char, int, long>>;
static_assert( std::is_same<T0, T1>::value, "!" );
}
为了回答您的问题("我在这里错过了什么?"(,您错过了typename
和::type
triangularize
在我看来,正确的版本应该是
template <class... _Pack>
struct triangularize {
// ..........VVVVVVVV add typename
using type = typename _triangularize_impl<std::tuple<_Pack...>,
std::index_sequence_for<_Pack...>>::type ;
// and add ::type ..........................................................^^^^^^
};
不幸的是,您的(更正的(代码似乎适用于 clang++,但不适用于 g++;我怀疑有一个 g++ 错误,但我不确定。
使用 Boost.Mp11 这是...不幸的是,不是单行。它需要几行。
我们定义一个函数来执行单个操作:给定所有内容的列表和下一个元素,附加该元素(也就是说,这将我们从第N
个解决方案带到第N+1
个解决方案(:
template <typename L, typename T>
using add_one = mp_push_back<L, mp_push_back<mp_back<L>, T>>;
现在折叠它 - 它只是依次为每个参数应用该二进制函数:
template <typename... Ts>
using triangularize_t = mp_fold<mp_list<Ts...>, tuple<tuple<>>, add_one>;
并检查它是否正确:
static_assert(std::is_same_v<triangularize_t<>,
tuple<tuple<>>>);
static_assert(std::is_same_v<triangularize_t<int>,
tuple<tuple<>, tuple<int>>>);
static_assert(std::is_same_v<triangularize_t<int, char>,
tuple<tuple<>, tuple<int>, tuple<int, char>>>);
我们可以将其推广到适用于任何类模板,而不仅仅是元组,方法是将triangularize
更改为使用输入列表并从输入参数推断其初始值:
template <typename L>
using triangularize_t = mp_fold<L, mp_push_back<mp_clear<L>, mp_clear<L>>, add_one>;
这也允许:
static_assert(std::is_same_v<triangularize_t<mp_list<int, char>>,
mp_list<mp_list<>, mp_list<int>, mp_list<int, char>>>);
或者您可能想要使用的任何其他列表(特别是不是variant
,因为variant<>
格式不正确(。
使用 Boost.Mp11 这是一个单行代码。我只是上次不够努力。此外,此解决方案符合OP的确切规格:
template <typename... Ts>
using triangularize_t =
mp_transform_q<
mp_bind_front<mp_take, std::tuple<Ts...>>,
mp_rename<mp_iota_c<sizeof...(Ts)>, std::tuple>
>;
莱姆解释这是做什么的,假设Ts...
是<int, char>
。
mp_iota_c<sizeof...(Ts)>
给出了序列mp_list<mp_int<0>, mp_int<1>>
。mp_rename
将一种"列表"类型换成另一种"列表"类型,在本例中mp_list
std::tuple
,因此您可以获得std::tuple<mp_int<0>, mp_int<1>>
。mp_bind_front<mp_take, std::tuple<Ts...>>
动态创建一个元函数,该元函数将接受一个参数并将其应用于完整tuple<Ts...>
上的mp_take
。mp_take
从给定列表中获取前N
件事。如果我们mp_int<1>
通过这个,在我们最初的tuple<int, char>
,我们会得到tuple<int>
。mp_transform_q
对列表中的每个元素调用提供的元函数。我们采用我们的tuple<mp_int<0>, mp_int<1>>
并将其扩展到tuple<mp_take<tuple<int, char>, mp_int<0>>, mp_take<tuple<int, char>, mp_int<1>>>
,这是tuple<tuple<>, tuple<int>>
。如愿以偿。
要将其更改为我的另一个答案(将<int>
三角化为tuple<tuple<>, tuple<int>>
(,我们可以将sizeof...(Ts)
更改为sizeof...(Ts)+1
。
为了扩展它以支持任何列表类型(不仅仅是tuple
(,我们可以更改这里的元函数来获取列表而不是包,并使用提供的列表类型作为解决方案。在某些方面,这使得解决方案更容易:
template <typename L>
using triangularize_t =
mp_transform_q<
mp_bind_front<mp_take, L>,
mp_append<mp_clear<L>, mp_iota<mp_size<L>>>
>;
template <typename... Ts>
using triangularize_t = triangularize_list<std::tuple<Ts...>>;
这里尴尬的部分是mp_append<mp_clear<L>, mp_iota<mp_size<L>>>
.基本上,我们需要序列列表与原始列表具有相同的列表类型。以前,我们可以使用mp_rename
因为我们知道我们需要一个元组。但是现在,我们没有将列表作为类模板 - 只有它的实例。可能有比mp_append<mp_clear<L>, U>
更好的方法可以做到这一点......但这就是我目前所拥有的。
我们可以通过使用多个参数包的部分专用化来避免使用任何index_sequence
。
#include <tuple>
template <typename Tuple, typename LastTuple, typename First, typename... Rest>
struct triangulate;
template <typename Tuple, typename LastTuple, typename Last>
struct triangulate<Tuple, LastTuple, Last> {
using type = Tuple;
};
template <typename First, typename Second, typename... TupleTypes, typename... LastTupleTypes, typename... Rest>
struct triangulate<std::tuple<TupleTypes...>, std::tuple<LastTupleTypes...>, First, Second, Rest...> {
using next = std::tuple<LastTupleTypes..., First>;
using type = typename triangulate<std::tuple<TupleTypes..., next>, next, Second, Rest...>::type;
};
template <typename... T>
using triangularize_t = typename triangulate<std::tuple<std::tuple<>>, std::tuple<>, T...>::type;
将最终产品作为第一个参数(std::tuple<std::tuples...>>
(传递,第二个参数是我们使用的最后一个std::tuple<...>
。
然后,我们递归地将下一个参数添加到最后一个元组,并将该元组添加到最终结果的末尾。
- C++:TypeDef使用元组
- Pybind11:将元组列表从Python传递到C++
- 重载元组索引运算符-C++
- 在C++中,如何通过几种类型从元组中选择多个元素
- 将fold表达式与std::一起用于两个元组
- std::ranges::elements_view,用于自定义类似元组的数据
- 将元组的向量转换/构造为堆
- 专用于 std 元组的模板,而无需用户执行remove_cvref
- 将元组的向量构造成堆
- 元组由 Swig 生成的 Python 包装器返回,用于C++向量
- 将元组类型扩展为可变参数模板?
- 时间复杂度 当具有复合数据类型(如元组或对)时?
- 类内部和外部静态 constexpr 元组之间的差异
- 可变参数模板与使用元组在参数中添加不同的数据对
- 访问和打印元组中的数据,并使用 C++14 使用模板函数显示数据
- boost::包含提升单元的元组的哈希值
- 我正在寻找一种优雅的方式来从元组向量创建tuple_element向量
- 如何在可变参数模板函数中遍历可变参数元组?
- 如何使用 SML 随机生成八进制元组
- 三角化元组