Home > Blockchain >  Enumerating a pack
Enumerating a pack

Time:03-24

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?

CodePudding user response:

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.

CodePudding user response:

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.

  • Related