[英]How to defer expanding a parameter pack?
I was toying around with tuples.我在玩元组。 I wanted to zip an arbitrary number of tuples.
我想 zip 任意数量的元组。 There is probably a better way to do that than what I came up with, but my solution led me to a problem that is interesting in itself.
可能有比我想出的更好的方法来做到这一点,但我的解决方案让我遇到了一个本身就很有趣的问题。
Sometimes, you want to expand one parameter pack at a time, and those parameter packs can be template arguments and function arguments in the same call.有时,您想一次扩展一个参数包,这些参数包可以是模板 arguments 和 function arguments 在同一个调用中。 I could not find an obvious way to expand
Is
without expanding ts
, besides stuffing ts
into a std::tuple
and unpacking it again using std::get<I>
.除了将
ts
填充到std::tuple
并使用std::get<I>
再次解包之外,我找不到扩展Is
而不扩展ts
的明显方法。
Obviously, it's preferable not to split one function into five functions.显然,最好不要将一个 function 拆分为五个函数。 I know I could use lambdas, but I'm not sure that would be any cleaner.
我知道我可以使用 lambda,但我不确定那会更干净。
Is there a nice way to defer expanding ts
?有没有一个很好的方法来推迟扩展
ts
?
https://godbolt.org/z/sY5xMTa7P https://godbolt.org/z/sY5xMTa7P
#include <iostream>
#include <tuple>
#include <string_view>
template <typename T, typename... Ts>
auto get_first(T t, Ts...) {
return t;
}
template <size_t I, typename... Ts>
auto zip2_impl4(Ts... ts) {
return std::make_tuple(std::get<I>(ts)...);
}
template <size_t I, typename T, size_t... Is>
auto zip2_impl3(T t, std::index_sequence<Is...>) {
return zip2_impl4<I>(std::get<Is>(t)...);
}
template <size_t I, typename T>
auto zip2_impl2(T t) {
using size = std::tuple_size<T>;
using seq = std::make_index_sequence<size::value>;
return zip2_impl3<I>(t, seq{});
}
template <size_t... Is, typename... Ts>
auto zip2_impl(std::index_sequence<Is...> seq, Ts... ts) {
// need to defer expanding the pack ts,
// because the packs Is and ts need to expand separately
auto t = std::make_tuple(ts...);
return std::make_tuple(zip2_impl2<Is>(t)...);
}
template <typename... Ts>
auto zip2(Ts... ts) {
using size = std::tuple_size<decltype(get_first(ts...))>;
using seq = std::make_index_sequence<size::value>;
return zip2_impl(seq{}, ts...);
}
int main() {
using namespace std::literals;
auto ints = std::make_tuple(1,2,3);
auto svs1 = std::make_tuple("a"sv, "b"sv, "c"sv);
auto svs2 = std::make_tuple("d"sv, "e"sv, "f"sv);
auto zipped = zip2(ints, svs1, svs2);
std::apply([](auto... args) {
(std::apply([](auto... args) {
((std::cout << args), ...);
}, args), ...);
}, zipped);
return 0;
}
output: 1ad2be3cf
If I understood you correctly: https://godbolt.org/z/e4fvr45rz如果我理解正确: https://godbolt.org/z/e4fvr45rz
#include <type_traits>
#include <tuple>
#include <cstddef>
template <size_t Indx,typename ... Tuples>
constexpr auto zip(Tuples ... tuples)
{
return std::make_tuple(std::get<Indx>(tuples)...);
}
template <typename ... Tuples, size_t ... Indx>
constexpr auto zip(std::index_sequence<Indx...>, Tuples... tuples)
{
// will expand simultaniously, not what you want...
//return std::tuple_cat(std::make_tuple(std::get<Indx>(tuples)...));
return std::tuple_cat(zip<Indx>(tuples...)...);
}
template <typename ... Tuples>
constexpr auto zip(Tuples ... tuples)
{
return zip(std::make_index_sequence<sizeof...(Tuples)>(), tuples...);
}
#include <iostream>
#include <string_view>
int main()
{
using namespace std::literals;
auto ints = std::make_tuple(1,2,3);
auto svs1 = std::make_tuple("a"sv, "b"sv, "c"sv);
auto svs2 = std::make_tuple("d"sv, "e"sv, "f"sv);
auto zipped = zip(ints, svs1, svs2);
std::apply([](auto ... args){
((std::cout << args), ...);
}, zipped);
return 0;
}
Output: Output:
Program returned: 0
1ad2be3cf
So there is a way, I guess.所以有办法,我猜。 You have to have your parameter packs in different contexts: one in the function arguments, another in the template argumetns.
您必须将参数包放在不同的上下文中:一个在 function arguments 中,另一个在模板参数中。 If you try to expand both packs in one "context" you will get a compile-time error:
如果您尝试在一个“上下文”中扩展这两个包,您将收到编译时错误:
// this will not compile
// return std::tuple_cat(std::make_tuple(std::get<Indx>(tuples)...)...);
// and this will expand them simultaniously, giving you "1bf" as a result
return std::tuple_cat(std::make_tuple(std::get<Indx>(tuples)...));
UPDATE: https://godbolt.org/z/9E1zj5q4G - more generic solution:更新: https://godbolt.org/z/9E1zj5q4G - 更通用的解决方案:
template <typename FirstTuple, typename ... Tuples>
constexpr auto zip(FirstTuple firstTuple, Tuples ... tuples)
{
return zip(std::make_index_sequence<std::tuple_size_v<FirstTuple>>(),firstTuple, tuples...);
}
int main()
{
using namespace std::literals;
auto ints = std::make_tuple(1,2,3);
auto svs1 = std::make_tuple("a"sv, "b"sv, "c"sv);
auto svs2 = std::make_tuple("d"sv, "e"sv, "f"sv);
auto svs3 = std::make_tuple("g"sv, "h"sv, "i"sv);
auto zipped = zip(ints, svs1, svs2, svs3);
std::apply([](auto ... args){
((std::cout << args), ...);
}, zipped);
return 0;
}
Outputs: 1adg2beh3cfi
输出:
1adg2beh3cfi
Instead of number of tuples you pass a number of elements.您传递的是许多元素,而不是元组的数量。 You have to b sure that the number of elements is the same for each tuple
您必须确保每个元组的元素数量相同
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.