#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 << ","; });
}
Question 1> Why I have to use std::integral_constant<std::size_t, Idx>{}
instead of simply Idx
in the following statement? Based on my understanding, std::integral_constant<std::size_t, Idx>
is a type. Is it true that std::integral_constant<std::size_t, Idx>{}
is a value of Idx
?
// OK
template <std::size_t... Idx>
auto make_index_dispatcher(std::index_sequence<Idx...>)
{
return [](auto&& f) { (f(std::integral_constant<std::size_t, Idx>{}), ...); };
}
// Error
template <std::size_t... Idx>
auto make_index_dispatcher(std::index_sequence<Idx...>)
{
return [](auto&& f) { (f(Idx), ...); };
}
Is it true that std::get
expected compile-time constant expression while Idx
is NOT a compile-time constant expression?
Question 2> Why we cannot pass std::index_sequence
by reference?
// Error:
auto make_index_dispatcher(std::index_sequence<Idx...>&)
Thank you
Why I have to use std::integral_constant{} instead of simply Idx in the following statement?
Because function arguments are never constant expressions. You can simply pass the indices as non-type template parameters instead, which are constant expressions. This works especially well with a C++20 template 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))); });
}
Why we cannot pass std::index_sequence by reference?
You can, but you need to invoke your function with an lvalue like with any other non-const
reference. This compiles:
template <std::size_t... Idx>
auto make_index_dispatcher(std::index_sequence<Idx...>&)
{
}
int main()
{
std::index_sequence<> is;
make_index_dispatcher(is);
}
Also, it's completely useless.
Also, your entire code can simply be:
int main()
{
std::apply([](auto&&... xs)
{
((std::cout << xs << ','), ...);
}, std::make_tuple(1, 42.1, "hi"));
}