枚举一个包

Enumerating a pack

我不太理解 Daisy Hollman 演讲中的基本技巧:

https://youtu.be/15etE6WcvBY?t=2670

使用带有显式模板参数的 c++20 lamdas 枚举包。

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

我的问题是这部分:

  (f(Idxs, args), ...);

在我看来,这就像我们“将 type 传递给函数”。好的,我们正在扩展包,所以它是单一类型而不是整个包,但在我看来它仍然是一个“类型”。更令人困惑的是,第二个参数 args 是局部变量的名称——同样是一个包,但仍然是一个变量。 args 的用法对我来说更有意义。

我原以为语法应该是:

  [&]<std::size_t... Idxs>(std::index_sequence<Idxs...> idxs) { (f(idxs, args), ...); }

请注意,我现在已经为参数包 (idxs) 命名并使用了它。这不会编译。

这里的语法是有点古怪,还是我遗漏了更深层次的东西?

这个

f(Idxs, args), ...

因为Idxsargs都是参数包,展开为:

f(0, args0), f(1,args1), .....

如果您将 Idxs 替换为 std::index_sequence<Idxs...> 那么它就不是一个包,它不会展开并且 f(std::index_sequence<Idxs...>, argX)f 的错误签名。 f 没有以 std::index_sequence<Idxs...> 作为第一个参数的重载。

索引序列的唯一目的是让您掌握索引。不使用索引序列本身,因此不需要命名。

正在使用单个参数调用 lambda:

(std::make_index_sequence<sizeof...(args)>{})

如果您打开 std::make_index_sequence 的文档,它会解释它生成 std::index_sequence<0, 1, 2, 3>,例如,如果 sizeof...(args) 是 4。

因此,在此示例中,lambda 的参数是 std::index_sequence<0, 1, 2, 3>

lambda 声明使用模板将 std::size_t... Idxs 推导为 0, 1, 2, 3,并且每一个都被馈送,一次一个,以 f() 作为其第一个参数。对 f() 的调用同时扩展了两个参数包,这些函数的参数,以及从对 lambda 的调用中推导出来的参数。