使用 lambdas 进行内联参数包解压缩

Version >= C++ 14

传统上,参数包解包需要在每次执行时编写辅助函数。

在这个玩具示例中:

template<std::size_t...Is>
void print_indexes( std::index_sequence<Is...> ) {
  using discard=int[];
  (void)discard{0,((void)(
    std::cout << Is << '\n' // here Is is a compile-time constant.
  ),0)...};
}
template<std::size_t I>
void print_indexes_upto() {
  return print_indexes( std::make_index_sequence<I>{} );
}

print_indexes_upto 想要创建和解包参数包索引。为此,它必须调用辅助函数。每次要解压缩创建的参数包时,最终都必须创建自定义帮助函数来执行此操作。

使用 lambdas 可以避免这种情况。

你可以将参数包解压缩为一组 lambda 的调用,如下所示:

template<std::size_t I>
using index_t = std::integral_constant<std::size_t, I>;
template<std::size_t I>
constexpr index_t<I> index{};

template<class=void, std::size_t...Is>
auto index_over( std::index_sequence<Is...> ) {
  return [](auto&& f){
    using discard=int[];
    (void)discard{0,(void(
      f( index<Is> )
    ),0)...};
  };
}

template<std::size_t N>
auto index_over(index_t<N> = {}) {
  return index_over( std::make_index_sequence<N>{} );
}

Version >= C++ 17

使用折叠表达式,index_over() 可以简化为:

template<class=void, std::size_t...Is>
auto index_over( std::index_sequence<Is...> ) {
  return [](auto&& f){
    ((void)(f(index<Is>)), ...);
  };
}

完成后,你可以使用它来替换必须手动解压缩参数包与其他代码中的第二个重载,让你解压缩参数包 inline

template<class Tup, class F>
void for_each_tuple_element(Tup&& tup, F&& f) {
  using T = std::remove_reference_t<Tup>;
  using std::tuple_size;
  auto from_zero_to_N = index_over< tuple_size<T>{} >();

  from_zero_to_N(
    [&](auto i){
      using std::get;
      f( get<i>( std::forward<Tup>(tup) ) );
    }
  );
}

通过 index_over 传递给 lambda 的 auto i 是一个 std::integral_constant<std::size_t, ???>。这有一个 constexpr 转换为 std::size_t,它不依赖于 this 的状态,因此我们可以将它用作编译时常量,例如当我们将它传递给 std::get<i> 时。

要回到顶部的玩具示例,请将其重写为:

template<std::size_t I>
void print_indexes_upto() {
  index_over(index<I>)([](auto i){
    std::cout << i << '\n'; // here i is a compile-time constant
  });
}

它更短,并在使用它的代码中保持逻辑。

现场演示