Search code examples
c++variadic-templatesc++20

Best way to apply a void function to a parameter pack


I would like to write a function that applies a function to each element of a parameter pack. The functions returns a std::tuple with the results of each invocation.

However, if the applied function returns void, I have to do something else, so I have a different overload for this case. But, almost all the ways I've found to expand the parameter pack do not work with void expressions, so I had to resort to what you see below, which seems a weird trick.

template<typename F, typename ...Args>
  requires requires { std::tuple{std::declval<F>(Args)...}; }
auto for_each(F f, Args ...args) {
  return std::tuple{f(args)...};
}

template<typename F, typename ...Args>
auto for_each(F f, Args ...args)
  [[maybe_unused]] int a[] = {(f(Members), 0)...};
}

Note that I have to declare a unused variable and mark it with the attribute. Which is the best way to obtain the expected result here?


Solution

  • This trick is the way to go pre-C++17, except that you need an extra , 0 in the array to support zero-length packs.

    In C++17 and newer, use a fold expression: (f(args), ...);.


    Note that you forgot perfect forwarding. You should be doing F &&f, Args &&... args, and then (f(std::forward<Args>(args)), ...);, and similarly for the first function.


    I also question the value of having such a function. I'd understand doing this to a tuple, but if you already have a pack, you can do this manually at the call site.