Использование выражения fold с std::apply для двух кортежей

Я только начал изучать выражения свертки С++ 17. Я понимаю, что к кортежу можно применить выражение сгиба, как в следующем примере (на основе ответов на этот вопрос):

#include <iostream>
#include <tuple>

int main() {
    std::tuple in{1, 2, 3, 4};
    std::cout << "in = ";
    std::apply([](auto&&... x) { ((std::cout << x << ' '), ...); }, in);
    std::cout << std::endl;

    std::multiplies<int> op;
    auto out = std::apply([&](auto&& ...x) { return std::tuple{op(x, 3)...}; }, in);
    std::cout << "out = ";
    std::apply([](auto&&... x) { ((std::cout << x << ' '), ...); }, out);
    std::cout << std::endl;
}

Выход:

in = 1 2 3 4
out = 3 6 9 12

Можно ли объединить два кортежа, используя аналогичный подход? Ссылаясь на приведенный выше пример, я хотел бы заменить константу 3 другим кортежем, таким как эта гипотетическая версия std::apply:

auto out = std::apply([&](auto&& ...x, auto&& ...y) { return std::tuple{op(x, y)...}; }, inX, inY);

В случае, если выражения fold неприменимы для этой цели, существует ли альтернативный метод для достижения того же результата в C++20 (кроме рекурсии шаблона и/oriSFINAE)?


person pmjobin    schedule 10.07.2020    source источник
comment
Но вам нужен линейный продукт или декартовый продукт? Я имею в виду... из 2 кортежей из 4 элементов вам нужен кортеж из 4 элементов или кортеж из 16 элементов?   -  person max66    schedule 10.07.2020
comment
Я просил линейный продукт, извините, если в моем вопросе не хватало точности.   -  person pmjobin    schedule 11.07.2020


Ответы (1)


Вы можете apply дважды:

auto out = std::apply([&](auto&&... x){
    return std::apply([&](auto&&... y){
        return std::make_tuple(op(x, y)...);
    }, inY);
}, inX);

Или вы можете использовать индексную последовательность, что проще в C++20, поскольку у нас более обобщенный лямбда-синтаксис, хотя и довольно плотный:

auto out = [&]<size_t... Is>(std::index_sequence<Is...>){
    return std::make_tuple(op(std::get<Is>(inX), std::get<Is>(inY))...);
}(std::make_index_sequence<std::tuple_size_v<decltype(inX)>>());

Возможно, стоит добавить помощника, например...

auto indices_for = []<typename... Ts>(std::tuple<Ts...> const&){
    return = []<size_t... Is>(std::index_sequence<Is...>){
        return [](auto f) -> decltype(auto) {
            return f(std::integral_constant<size_t, Is>()...);
        };
    }(std::index_sequence_for<Ts...>());
};

То есть indices_for(t) дает вам функцию, которая принимает функцию и вызывает ее с набором целочисленных констант. Это беспорядок, но это беспорядок, который вы должны написать один раз и можете легко протестировать. Это позволяет вам написать:

auto out = indices_for(inX)([&](auto... Is){
    return std::make_tuple(op(std::get<Is>(inX), std::get<Is>(inY))...);
});

Что-то такое.

person Barry    schedule 10.07.2020
comment
Ух ты! Мне никогда не приходило в голову, что я могу просто вложить вызовы в std::apply... Большое спасибо! Спасибо также за то, что нашли время предложить альтернативный метод. - person pmjobin; 11.07.2020