单行std :: get std :: index_sequence?

时间:2019-01-02 22:04:12

标签: c++ stdtuple

我有一个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进行呼叫,才能“解开” index_sequence来呼叫std::get...

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

也就是说,直接在调用站点上打开元组吗?

1 个答案:

答案 0 :(得分:2)

如果您不想或不能使用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