Is it possible to have a function(-name) as a template parameter in C++?
Solution 1
Your idea's ok, but you're not passing in a type but a value (specifically, a function pointer>. Alternatively, pass a template policy providing functions - it's a good idea to read Modern C++ Design by Andrei Alexandrescu.
#include <iostream>
int f(int x) { return 2 * x; }
int g(int x) { return -3 * x; }
typedef int (*F)(int);
template<F f>
int do_work()
{
return f(7);
}
int main()
{
std::cout << do_work<f>() << '\n'
<< do_work<g>() << '\n';
}
OR
int calculate() { return 4; }
struct F { int do_something_with(int x) { return 2 * x; } };
struct G { int do_something_with(int x) { return -3 * x; } };
// or, make these functions static and use Operator::do_something_with() below...
template<typename Operation>
int do_work()
{
int v = calculate(7);
return Operation().do_something_with(v);
}
int main()
{
std::cout << do_work<F>() << '\n'
<< do_work<G>() << '\n';
}
Solution 2
You can have pointers to functions as template parameters, but function objects are more "C++ish". However, you can write your function template in a way that accepts both variants:
#include <iostream>
void f(int x)
{
std::cout << "inside function f\n";
}
struct g
{
void operator()(int x)
{
std::cout << "inside function object g\n";
}
};
template <typename Functor>
void do_work(Functor fun)
{
fun(42);
}
int main()
{
// template argument is automatically deduced
do_work(&f);
// but we could also specify it explicitly
do_work<void(*)(int)>(&f);
// template argument is automatically deduced
do_work(g());
// but we could also specify it explicitly
do_work<g>(g());
}
Here, the name Functor
hints at any type that is callable via f(x)
syntax. Functions support this syntax naturally, and in the case of function objects, f(x)
is syntactic sugar for f.operator()(x)
.
Solution 3
No, you need to wrap the functions in a wrapper class with operator()
. Here is an example:
class Functor_f
{
public:
void operator()(int x)
{
}
};
class Functor_g
{
public:
void operator()(int x)
{
}
};
template<typename F>
void do_work()
{
F f;
int v = calculate();
f(v);
}
int main()
{
do_work<Functor_f>();
do_work<Functor_g>();
}
You can use std::ptr_fun
to do this wrapping automatically for you. For example:
void f(int x)
{
}
void g(int x)
{
}
template<typename F>
void do_work(F f)
{
int v = calculate();
f(v);
}
int main()
{
do_work(std::ptr_fun(f));
do_work(std::ptr_fun(g));
}
Martin Ba
C++ Link of the day: Stop Software Patents (by http://www.ffii.org/)
Updated on June 06, 2022Comments
-
Martin Ba almost 2 years
I don't want function pointer overhead, I just want the same code for two different functions with the same signature:
void f(int x); void g(int x); ... template<typename F> void do_work() { int v = calculate(); F(v); } ... do_work<f>(); do_work<g>();
Is this possible?
To clear up possible confusion: With "template parameter" I mean the parameter/argument to the template and not a function parameter whose type is templated.
-
Doug over 13 years+1 ...and they compile to a direct call, without function-pointer calling overhead at runtime.
-
MSalters over 13 years@Doug: that's not guaranteed. Highly likely, though, since e.g.
std::sort
benefits a lot from that optimization. -
Tony Delroy over 13 yearsYour implementation there will match the
typename Fun
parameter to the function prototypevoid(*)(int)
, and then use the same instantiation for both calls - not inlining: that's why you actually end up passing the function pointer as a run-time argument todo_work(Fun fun)
. -
Martin Ba over 13 years+1 for showing a decent solution with fn-pointers. But I assume this would still incur some runtime overhead to dereference the fn pointer vs. a copy&paste version for do_work_f() and do_work_g() ... ?
-
Tony Delroy over 13 years@Martin: it's a crucial question - all up the optimiser. I wouldn't bet on this being inlined, whereas I would bet on a policy template member....
-
Martin Ba over 13 years@Tony - could you maybe link in some resource that (approx.) describes a template policy thing in the way you would adopt it for this problem?
-
Cheers and hth. - Alf over 13 yearsThe first example (without
std::ptr_fun
) gives a much better chance of actual inlining -- and (although of course it depends on a lot) is thus most likely fastest, somewhat counter-intuitive perhaps. Cheers, -
Eamon Nerbonne over 13 years+1 - does not deserve -1: But it could accept a function object just fine; this is the flexible, standard approach to use.
-
Eamon Nerbonne over 13 years
return Operation(7);
should bereturn Operation()(7);
-
Stephane Rolland over 13 years+1; Just like Martin asked, it would be great to have your implementation of the policy template member. I'm still struggling with much of the "C++ Modern Design" book which I don't really grasp. Any concrete application example is dearly welcome.
-
Doug over 13 years@MSalters - definitely not guaranteed. I misread the FredOverflow's answer slightly - I thought FredOverflow had
do_work
accepting a template non-type function pointer argument, as the question indicates, rather than a template type argument. The non-type arguments are even more likely to compile into a direct call. -
fredoverflow over 13 years@Martin: I have updated my answer, would you reconsider the downvote?
-
Tony Delroy over 13 years@Martin: I changed the template policy version above a little to more directly mirror the question. While it is a little more verbose, I tend to use function names for the things the policy provides - it is common for there to be more than one function in each policy, and it often makes for more comprehensible code. , @Stephane Rolland: for a more great concrete application, consider
std::sort
's acceptance of a less-than policy... by not hard-coding operator<, it allows you to say compare dereferenced pointers or reverse the sort order. Is that a useful practical example?