简体   繁体   English

如何推迟扩展参数包?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM