Reputation: 1156
I don't quite understand the base trick from Daisy Hollman's talk:
https://youtu.be/15etE6WcvBY?t=2670
Enumerating a pack using c++20 lamdas with explicit template arguments.
#include <iostream>
#include <utility>
template <typename Function, typename ...Ts>
void enumerate_pack(Function f, Ts... args) {
[&]<std::size_t... Idxs>(std::index_sequence<Idxs...>) { (f(Idxs, args), ...); }
(std::make_index_sequence<sizeof...(args)>{});
}
int main() {
enumerate_pack([](std::size_t i, auto arg) { std::cout << i << ": " << arg << "\n"; }, "hello",
42, "world", 73.2);
}
My problem with it is this part:
(f(Idxs, args), ...);
This looks to me like we are "passing a type to a function". OK, we are expanding the pack so it's a single type and not the whole pack, but it's still a "type", to my eyes. And even more confusingly, the second argument args
is the name of a local variable - again a pack, but a variable nonetheless. The usage of args
makes much more sense to me.
I would have thought the syntax should be:
[&]<std::size_t... Idxs>(std::index_sequence<Idxs...> idxs) { (f(idxs, args), ...); }
Note that I have now given a name to the parameter pack (idxs
) and used that. This doesn't compile.
Is the syntax here just a little baroque, or is there something deeper I am missing?
Upvotes: 1
Views: 109
Reputation: 118415
The lambda is being called with a single parameter:
(std::make_index_sequence<sizeof...(args)>{})
If you open the documentation for std::make_index_sequence
, it will explain that it produces a std::index_sequence<0, 1, 2, 3>
, for example, if sizeof...(args)
is 4.
So, in this example, the parameter to the lambda is a std::index_sequence<0, 1, 2, 3>
.
The lambda declaration uses a template to deduce std::size_t... Idxs
as 0, 1, 2, 3
, and each one of these get fed, one at a time, to f()
as its first parameter. The call to f()
simultaneously expands two parameter packs, the parameters to these functions, and the one that's deduced from the call to the lambda.
Upvotes: 1
Reputation: 122657
This
f(Idxs, args), ...
Because both Idxs
and args
are parameter packs, expands to:
f(0, args0), f(1,args1), .....
If you replace Idxs
with a std::index_sequence<Idxs...>
then it isnt a pack, it does not expand and f(std::index_sequence<Idxs...>, argX)
is the wrong signature for f
. There is no overload for f
that takes a std::index_sequence<Idxs...>
as first paramter.
The only purpose of the index sequence is to get your hands on the indices. The index sequence itself is not used, hence needs not be named.
Upvotes: 1