Section 9.6.  (Member) Function Pointers as Template Arguments
Team LiB
Previous Section Next Section

9.6. (Member) Function Pointers as Template Arguments

Integral 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:

  • Pointers and references to specific functions

  • Pointers and references to statically stored data

  • Pointers to member functions

  • And pointers to data members

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.

  • Because values of class type are not legal template parameters, compose_fg2 can't be used to compose arbitrary function objects (but see exercise 9-4).

  • There's no way to build an object generator function for compose_fg2. An object generator would have to accept the functions to be composed as function arguments and use those values as arguments to the compose_fg2 template:



    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]

[7] Language extensions that would bypass this limitation are currently under discussion in the C++ standardization community, so watch for progress in the next few years.

    Team LiB
    Previous Section Next Section