9.6. (Member) Function Pointers as Template ArgumentsIntegral constants are not the only kind of non-type template parameters. In fact, almost any kind of value that can be determined at compile time is allowed, including:
We can achieve dramatic efficiency gains by using these kinds of template parameters. When our earlier compose_fg class template is used on two function pointers, it is always at least as large as the pointers themselves: It needs to store the values. When a function pointer is passed as a parameter, however, no storage is needed at all. To illustrate this technique, let's build a new composing function object template:
template <class R, class F, F f, class G, G g>
struct compose_fg2
{
typedef R result_type;
template <class T>
R operator()(T const& x) const
{
return f(g(x));
}
};
Note, in particular, that compose_fg2 has no data members. We can use it to compute sin2(log2(x)) for each element of a sequence:
#include <functional>
#include <algorithm>
#include <cmath>
float input[5] = {0.0, 0.1, 0.2, 0.3, 0.4};
float output[5];
inline float log2(float x) { return std::log(x)/std::log(2); }
typedef float (*floatfun)(float);
float* ignored = std::transform(
input, input+5, output
, compose_fg2<float, floatfun,sin_squared, floatfun,log2>()
);
Don't be fooled by the fact that there are function pointers involved here: on most compilers, you won't pay for an indirect function call. Because it knows the precise identity of the functions indicated by f and g, the compiler should optimize away the empty compose_fg2 object passed to std::transform and generate direct calls to log2 and sin_squared in the body of the instantiated transform algorithm. For all its efficiency benefits, compose_fg2 comes with some notable limitations.
template <class R, class F, class G>
compose_fg2<R,F,f,G,g> compose(F f, G g)
{
return compose_fg2<R,F,f,G,g>(); // error
}
Unfortunately, any value passed to a function enters the runtime world irretrievably. At that point, there's no way to use it as an argument to a class template without causing a compiler error.[7]
|