Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::integer_sequence in a lambda before C++20

I have started using this type of construction, which depends on C++20's explicit template parameters for lambdas:

template<typename... Ts>
struct Foo
{
  std::tuple<Ts...> bars;

  auto get_labels(const std::array<std::size_t,sizeof...(Ts)>& indices) const
  {
    // construct tuple from bar labels
    return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
        return std::make_tuple(std::get<Is>(bars).get_label(indices[Is])...);
      }(std::index_sequence_for<Ts...>{});
  }
};

Compiler Explorer example

How can I do this in C++17 or C++14 (without explicit template parameters for lambdas)?

like image 482
kc9jud Avatar asked Sep 01 '25 15:09

kc9jud


1 Answers

So it's important to recognize what part you actually need. When you write this:

// construct tuple from bar labels
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
    return std::make_tuple(std::get<Is>(bars).get_label(indices[Is])...);
}(std::index_sequence_for<Ts...>{});

What you actually need is Is.... Or, more generally, what you actually need is a pack of constant values. In the above example, you are accepting a parameter of index_sequence<Is...> - that's one parameter, whose type has the pack of constant values in it.

But a different way to do it would be to accept N different parameters, where the first parameter is of type integral_constant<size_t, 0> and the second parameter is of type integral_constant<size_t, 1>, and so forth. If you could produce those arguments, then the lambda part becomes

[&](auto... Is){
    return std::make_tuple(std::get<Is>(bars).get_label(indices[Is])...);
}

Note that the body is identical, I just changed what the parameter(s) looks like. And this is now a valid C++14 lambda.

So the rest of the problem is producing the function template with<N>(f), which calls f(integral_constant<size_t, 0>{}, integral_constant<size_t, 1>{},..., integral_constant<size_t, N-1>{}) which allows you to call:

return with<sizeof...(Ts)>([&](auto... Is){
    return std::make_tuple(std::get<Is>(bars).get_label(indices[Is])...);
})

And writing with in C++14 is straightforward. It's really just the same index_sequence trick, with an extra indirection (cause you need to then change the one index_sequence into the N integral_constants). Arguably, the result looks better too - it's less busy. I prefer this in C++20 anyway.

like image 177
Barry Avatar answered Sep 05 '25 06:09

Barry