C++: использование std::apply для перебора кортежа в двух частях: до индекса, применяющего лямбда, и затем после индекса, применяющего другую лямбда

Если я хочу перебрать кортеж, используя std::apply но не применять одну функцию ко всему, как я могу разделить кортеж, т.е. применить одну функцию к первой n значения и другой для всех значений после него?

some_values будет кортеж, который может иметь любую длину и типы и length_of_first_part (вероятно, названный немного менее подробно) будет известен во время компиляции.

std::tuple<char, long long, double, long double, float> some_values(33, 2, 3.4, 5.6, 7.8);
const size_t length_of_first_part = 2;
std::apply(
    [](auto&&... current_val) {
        ((std::cout << "(Should be first part) " << current_val << "\n"), ...); //Would obviously do a litle more than cout, but this is just a minimal example
    }, some_values
);

std::apply(
    [](auto&&... current_val) {
        ((std::cout << "(Should be second part) " << current_val << "\n"), ...);
    }, some_values
);

1 ответ

Решение

Сделал снимок. Посмотрите на "возможную реализацию" здесь: https://en.cppreference.com/w/cpp/utility/apply

Он проходит std::index_sequence к вспомогательной функции для вызова std::invoke. Я изменил это пройти std::make_sequence<N>(N - индекс разделения) вместо размера кортежа. Затем я передал обратный (размер кортежа - N) для вызова std::invoke по второй функции:

template <int N, typename F1, typename F2, typename Tuple, size_t... I1s, size_t... I2s>
decltype(auto) split_apply_impl(F1&& f1, F2&& f2, Tuple&& t, std::index_sequence<I1s...>, std::index_sequence<I2s...>) {
    std::invoke(std::forward<F1>(f1), std::get<I1s>(std::forward<Tuple>(t))...);
    return std::invoke(std::forward<F2>(f2), std::get<(I2s+N)>(std::forward<Tuple>(t))...);
}

template <size_t N, typename F1, typename F2, typename Tuple>
decltype(auto) split_apply(F1&& f1, F2&& f2, Tuple&& t)
{
    return split_apply_impl<N>(
        std::forward<F1>(f1), 
        std::forward<F2>(f2),
        std::forward<Tuple>(t),
        std::make_index_sequence<N>{},
        std::make_index_sequence<(std::tuple_size_v<std::remove_reference_t<Tuple>>-N)>{});
}

Применение:

std::tuple<char, long long, double, long double, float> some_values(33, 2, 3.4, 5.6, 7.8);
constexpr size_t length_of_first_part = 2;
split_apply<length_of_first_part>(
    [](auto&&... current_val) { 
        ((std::cout << "(Should be first part) " << current_val << "\n"), ...); 
    },
    [](auto&&... current_val) {
        ((std::cout << "(Should be second part) " << current_val << "\n"), ...);
    },
    some_values);

Подтверждение концепции: https://godbolt.org/z/6dvMre

Другие вопросы по тегам