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

问题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>{}), ...); };
}

// 错误

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

std::get预期的编译时常量表达式 whileIdx不是编译时常量表达式是真的吗?

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

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

谢谢

4

3 回答 3

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

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

而且,它完全没用。


此外,您的整个代码可以简单地是:

int main() 
{
    std::apply([](auto&&... xs)
    {
        ((std::cout << xs << ','), ...);
    }, std::make_tuple(1, 42.1, "hi"));
}
于 2019-08-07T14:52:07.547 回答
4

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

std::get 预期的编译时常量表达式是真的,而 Idx 不是编译时常量表达式吗?

std::get<>需要一个编译时表达式,但您实际上并没有idx直接使用(这不是编译时表达式)。

std::integral_constant<std::size_t, I>有一个方便的constexpr转换运算符std::size_t返回I,所以当你这样做时:

std::get<idx>(...)

...你实际上是在做std::get<(std::size_t)idx>, 并且(std::size_t)idx是一个编译时表达式,因为转换运算符是constexpr.

如果你不包裹Idx在 astd::integral_constant中,则通用 lambda 中的类型idx将是std::size_t,并且以上所有内容都将不起作用。

于 2019-08-07T14:55:40.800 回答
2

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

纯娱乐...

在 C++17 之前,您所说的都是正确的(据我所知)(因为std::get()模板参数必须是已知的编译时间,并且使用std::integral_constant是一种优雅的方式来传递编译时已知的常量作为auto参数类型通用(C++14 或更高版本)lambda;请参阅其他答案以获得更清晰的解释)但从std::integral_constantC++20 开始不再正确(不一定必须使用)。

实际上,C++20 引入了模板 lambda,因此您可以如下重写代码,将Idx值作为模板参数传递,而无需std::integral_constant

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

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

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]<std::size_t I>() { f(std::get<I>(std::forward<Tuple>(t))); });
} // ..................^^^^^^^^^^^^^^^^^ template parameter added, argument removed

int main() 
{
    for_each(std::make_tuple(1, 42.1, "hi"), [](auto&& e) {std::cout << e << ","; });
}
于 2019-08-07T18:43:45.820 回答