A C++ template function that runs a given callable (function or lambda) on each of its arguments, with compatibility for pre-C++17 compilers.

The Problem

Sometimes you need to apply the same function or operation to multiple arguments of possibly different types. While C++17 provides fold expressions for this, you may need to support older compilers.

The Solution

Here’s a template function that applies a callable to each argument:

For C++17 and later

With C++17 fold expressions, this becomes elegantly simple:

template<typename F, typename ...T>
void for_each_arg(F&& function, T&&... args) {
    (function(std::forward<T>(args)), ...);
}

For pre-C++17 compilers

For compatibility with older standards, use this workaround:

template<typename F, typename ...T>
void for_each_arg(F&& function, T&&... args){
    int pass[]{ ((function(std::forward<T>(args)), 0)... };
    (void)pass;
}

How It Works

C++17 Version

  • Uses a fold expression with the comma operator
  • std::forward<T>(args) ensures perfect forwarding
  • The fold expression expands to function(arg1), function(arg2), ...

Pre-C++17 Version

  • Creates an array using brace initialization
  • The comma operator ensures function(arg) is called, then 0 is stored in the array
  • (void)pass suppresses unused variable warnings
  • The pack expansion ... applies this to all arguments

Usage Example

#include <iostream>

// Example callable
auto print = [](const auto& value) {
    std::cout << value << " ";
};

int main() {
    for_each_arg(print, 1, 2.5, "hello", 'c');
    // Output: 1 2.5 hello c
    return 0;
}

Key Features

  • Perfect forwarding: Preserves value categories of arguments
  • Template metaprogramming: Works with any callable and argument types
  • Backward compatibility: Supports pre-C++17 compilers
  • Zero runtime overhead: Everything is resolved at compile time

This technique is useful for debugging, logging, or any scenario where you need to apply the same operation to multiple heterogeneous arguments.