为什么在爆炸std :: tuple时需要std :: integral_constant?

q09*_*987 2 c++ template-meta-programming variadic-templates generic-lambda c++17

#include <iostream>

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

template <std::size_t... Idx>
auto make_index_dispatcher(std::index_sequence<Idx...>) 
{
    return [](auto&& f) { (f(std::integral_constant<std::size_t, Idx>{}), ...); };
}

template <std::size_t N>
auto make_index_dispatcher() 
{
    return make_index_dispatcher(std::make_index_sequence<N>{});
}

template <typename Tuple, typename Func>
void for_each(Tuple&& t, Func&& f) 
{
    constexpr auto n = std::tuple_size<std::decay_t<Tuple>>::value;
    auto dispatcher = make_index_dispatcher<n>();
    dispatcher([&f, &t](auto idx) { f(std::get<idx>(std::forward<Tuple>(t))); });
}

int main() 
{
    for_each(std::make_tuple(1, 42.1, "hi"), [](auto&& e) {std::cout << e << ","; });
}
Run Code Online (Sandbox Code Playgroud)

问题1>为什么在下面的语句中我必须使用std::integral_constant<std::size_t, Idx>{}而不是简单地使用Idx?根据我的理解,std::integral_constant<std::size_t, Idx>是一种。这是真的std::integral_constant<std::size_t, Idx>{}Idx吗?

// 好

template <std::size_t... Idx>
auto make_index_dispatcher(std::index_sequence<Idx...>) 
{
    return [](auto&& f) { (f(std::integral_constant<std::size_t, Idx>{}), ...); };
}
Run Code Online (Sandbox Code Playgroud)

//错误

template <std::size_t... Idx>
auto make_index_dispatcher(std::index_sequence<Idx...>) 
{
    return [](auto&& f) { (f(Idx), ...); };
}
Run Code Online (Sandbox Code Playgroud)

确实std::get预期的编译时常量表达式Idx不是编译时常量表达式吗?

问题2>为什么我们不能通过std::index_sequence引用?

//错误: auto make_index_dispatcher(std::index_sequence<Idx...>&)

谢谢

Vit*_*meo 6

为什么在以下语句中我必须使用std :: integral_constant {}而不是简单的Idx?

因为函数参数永远不是常量表达式。您可以简单地将索引作为非类型模板参数传递,它们是常量表达式。这对于C ++ 20模板lambda尤其有效:

template <std::size_t... Idx>
auto make_index_dispatcher(std::index_sequence<Idx...>) 
{
    return [](auto&& f) { (f.template operator()<Idx>(), ...); };
}

template <typename Tuple, typename Func>
void for_each(Tuple&& t, Func&& f) 
{
    constexpr auto n = std::tuple_size<std::decay_t<Tuple>>::value;
    auto dispatcher = make_index_dispatcher<n>();
    dispatcher([&f, &t]<auto Idx>(){ f(std::get<Idx>(std::forward<Tuple>(t))); });
}
Run Code Online (Sandbox Code Playgroud)

Godbolt.org上的实时示例


为什么我们不能通过引用传递std :: index_sequence?

可以,但是您需要像其他所有非const引用一样使用左值调用函数。这样编译:

template <std::size_t... Idx>
auto make_index_dispatcher(std::index_sequence<Idx...>&) 
{
}

int main()
{
    std::index_sequence<> is;        
    make_index_dispatcher(is);
}
Run Code Online (Sandbox Code Playgroud)

而且,它完全没有用。


同样,您的整个代码可以简单地是:

int main() 
{
    std::apply([](auto&&... xs)
    {
        ((std::cout << xs << ','), ...);
    }, std::make_tuple(1, 42.1, "hi"));
}
Run Code Online (Sandbox Code Playgroud)