使用 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
  });
}

它更短,並在使用它的程式碼中保持邏輯。

現場演示