однострочный std :: get std :: index_sequence? - PullRequest
0 голосов
/ 03 января 2019

У меня есть std::tuple, и я хочу развернуть содержимое, используя std::index_sequence, чтобы вызвать шаблон функции variadic

Рассмотрим следующий пример кода:

#include <iostream>
#include <tuple>

template<typename... Ts>
void foo(const std::string& s, Ts... ts)
{
    std::cout << "foo called with " << s << " and " << sizeof...(Ts) << " ts\n";
}

template<typename Tuple, std::size_t... Ixs>
void call_foo(const std::string& s, Tuple& t, std::index_sequence<Ixs...>)
{
    foo(s, std::get<Ixs>(t)...);
}

template<typename... Ts>
struct Bar
{
    Bar(Ts... ts) : t(ts...)
    { }

    void do_it()
    {
        call_foo("hi", t, std::make_index_sequence<std::tuple_size<decltype(t)>::value>{});
    }

    std::tuple<Ts...> t;
};

template<typename... Ts> Bar<Ts...> make_bar(Ts... ts) { return Bar<Ts...>(ts...); }

int main ()
{
    auto bar = make_bar(1, 'a', 2.3);
    bar.do_it();
}

Обратите внимание, что мне нужно позвонить через call_foo с моим index_sequence, чтобы "развернуть" index_sequence для вызова std::get...

Можно ли отказаться от промежуточного звена call_foo функция, а вызов foo напрямую?

То есть развернуть кортеж прямо на сайте вызова?

1 Ответ

0 голосов
/ 03 января 2019

Если вы не хотите или не можете использовать std::apply, я могу предложить несколько альтернатив.

Приведенные ниже фрагменты основаны на предыдущей редакции вашего вопроса, в которой не было class Bar. Те же самые решения работают и для новой редакции.

(1) Вы можете заменить call_foo лямбда-выражением C ++ 20 с явным списком параметров шаблона:

#include <cstddef>
#include <iostream>
#include <tuple>
#include <utility>

template<typename... Ts>
void foo(const std::string& s, Ts... ts)
{
    std::cout << "foo called with " << s << " and " << sizeof...(Ts) << " ts\n";
}

template<typename... Ts>
void bar(Ts... ts)
{
    const std::string s = "hello world";
    const auto t = std::make_tuple(ts...);

    [&]<std::size_t ...I>(std::index_sequence<I...>)
    {
        foo(s, std::get<I>(t)...);
    }
    (std::make_index_sequence<std::tuple_size_v<decltype(t)>>{});
}

int main()
{
    bar(1, 'a', 2.3);
}

Попробуй вживую

К сожалению, в настоящее время GCC 8, похоже, является единственным крупным компилятором, который их поддерживает.


(2) Если у вашего компилятора нет новых причудливых лямбд, или вы не хотите писать шаблон index_sequence каждый раз, когда вам нужно расширить кортеж, я предлагаю следующее:

#include <cstddef>
#include <iostream>
#include <tuple>
#include <utility>

template <std::size_t ...I, typename F> void with_sequence_impl(F &&func, std::index_sequence<I...>)
{
    func(std::integral_constant<std::size_t, I>{}...);
}

template <std::size_t N, typename F> void with_sequence(F &&func)
{
    with_sequence_impl(std::forward<F>(func), std::make_index_sequence<N>{});
}

template<typename... Ts>
void foo(const std::string& s, Ts... ts)
{
    std::cout << "foo called with " << s << " and " << sizeof...(Ts) << " ts\n";
}

template<typename... Ts>
void bar(Ts... ts)
{
    const std::string s = "hello world";
    const auto t = std::make_tuple(ts...);

    with_sequence<std::tuple_size_v<decltype(t)>>([&](auto ... i)
    {
        foo(s, std::get<i.value>(t)...);
    });
}

int main()
{
    bar(1, 'a', 2.3);
}

Попробуй в живую

...