c++templatescode-generationfunctor

Function passed as template argument


I'm looking for the rules involving passing C++ templates functions as arguments.

This is supported by C++ as shown by an example here:

#include <iostream>
void add1(int &v) { v += 1; } 
void add2(int &v) { v += 2; }

template <void (*T)(int &)>
void doOperation()
{
  int temp = 0;
  T(temp);
  std::cout << "Result is " << temp << std::endl;
}

int main()
{
    doOperation<add1>();
    doOperation<add2>();
}

Learning about this technique is difficult, however. Googling for "function as a template argument" doesn't lead to much. And the classic C++ Templates The Complete Guide surprisingly also doesn't discuss it (at least not from my search).

The questions I have are whether this is valid C++ (or just some widely supported extension).

Also, is there a way to allow a functor with the same signature to be used interchangeably with explicit functions during this kind of template invocation?

The following does not work in the above program, at least in Visual C++, because the syntax is obviously wrong. It'd be nice to be able to switch out a function for a functor and vice versa, similar to the way you can pass a function pointer or functor to the std::sort algorithm if you want to define a custom comparison operation.

struct add3 {
    void operator() (int &v) {v += 3;}
};
...

doOperation<add3>();

Pointers to a web link or two, or a page in the C++ Templates book would be appreciated!


Solution

  • Yes, it is valid.

    As for making it work with functors as well, the usual solution is something like this instead:

    template <typename F>
    void doOperation(F f)
    {
      int temp = 0;
      f(temp);
      std::cout << "Result is " << temp << std::endl;
    }
    

    which can now be called as either:

    doOperation(add2);
    doOperation(add3());
    

    See it live

    The problem with this is that if it makes it tricky for the compiler to inline the call to add2, since all the compiler knows is that a function pointer type void (*)(int &) is being passed to doOperation. (But add3, being a functor, can be inlined easily. Here, the compiler knows that an object of type add3 is passed to the function, which means that the function to call is add3::operator(), and not just some unknown function pointer.)