Below some example (commented) code to show how functions can be used in a template. You can replace typename Parameter
with typename... Parameter
and p
to p...
if you want the first template to work for any number of fucntion arguments, which uses variadic templates.
#include <functional> // for std::function
#include <iostream>
// example functions
void func(int i)
{
std::cout << "Integer: " << i << ".\n";
}
int gunc(int i)
{
int result = i+1;
std::cout << "+Integer:" << result << ".\n";
return result;
}
// general non-restrictive template
template<typename Function, typename Parameter>
void call_twice(Function f, Parameter p)
{
f(p);
f(p);
}
// Restrict signature to void(int), but the return value can be anything: it will be ignored
void call_thrice(std::function<void(int)> f, int p)
{
f(p);
f(p);
f(p);
}
// Restrict signature to int(int), to exclude func
void call_four_times(std::function<int(int)> f, int p)
{
f(p);
f(p);
f(p);
f(p);
}
int main()
{
call_twice(func, 1); // instantiates void call_twice(void (*)(int), int)
call_twice(gunc, 1); // instantiated void call_twice(int (*)(int), int)
// Note I do not write the explicit types of func and gunc in the above comments, they're not important!
call_thrice(func, 10); // converts func to a std::function<void(int)>
call_thrice(gunc, 10); // return value can be ignored, so int(int) is convertible to void(int)
//call_four_times(func, 100); // will fail to compile: cannot convert a function of signature void(int) to one with signature int(int)
call_four_times(gunc, 100); // converts gunc to std::function<int(int)>
}
Live demo here.
The basic form of a template declaration starts anything of the form
template<typename T>
template<class T> // same as above
template<int N> // explicitly typed template parameter, need not be an int
template<typename... variadic_template_args>
And of course the forms with combinations of the above, including nested template types and all. Just remember the variadic template parameter must come last.
Templates are what the name implies: a blueprint to create a class with certain functionality expressed within the template definition.
Instantiation occurs when you actually call a template function (from either another instantiated template function or a non-template function), or for class templates, when you declare an object of the type, e.g. for std::vector
:
std::vector<double> v; // instantiate the class template std::vector for type double
This is no different to templates that take functions as arguments. Note that functions decay to function pointers when passed to another function.
For example, a class template with a function as template parameter:
#include <iostream>
#include <type_traits> // for std::result_of
template<typename Function, Function f>
struct A
{
using function_type = Function;
template<typename... ArgTypes>
typename std::result_of<Function(ArgTypes...)>::type call(ArgTypes... args)
{
return f(args...);
}
};
void func(int i) { std::cout << "Integer: " << i << ".\n"; }
int main()
{
A<decltype(&func), func> a;
a.call(42);
}
Live demo here. Note the extra &
in the decltype
, because a function only decays to a function pointer when it is passed to a function, not when it is used as a template parameter. Note the "extra" typename
before the std::result_of
: this is required because its type
typedef is a dependent name. These and other times you need to use the template
and typename
keywords, those are covered in Where and why do I have to put the “template” and “typename” keywords?