单行std::getstd::index_sequence?

single-line std::get std::index_sequence?

我有一个 std::tuple,我想使用 std::index_sequence 解包内容以调用可变函数模板

考虑以下示例代码:

#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 进行调用,以便 "unwrap" 调用 index_sequence std::get...

是否可以放弃中间的call_foo函数,直接调用foo

也就是说,直接在调用点解包元组?

如果您不想或不能使用 std::apply,我可以推荐一些替代方案。

下面的片段是基于你的问题的先前修订,其中没有 class Bar。相同的解决方案也适用于新版本。

(1) 您可以将 call_foo 替换为具有显式模板参数列表的 C++20 lambda:

#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);
}

Try it live

不幸的是,GCC 8 目前似乎是唯一支持这些的主要编译器。


(2) 如果您的编译器没有新的花式 lambda,或者您不想每次需要时都编写 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);
}

Try it live