I l@ve RuBoard Previous Section Next Section

5.7 Build One, Get One Free

Reading the previous section, you may have asked yourself, Why didn't we start with implementing support for pointers to regular functions, which seems to be the simplest case? Why did we jump directly to functors, templates, and so on? The answer is simple: By now, support for regular functions is already implemented. Let's modify the test program a bit:



#include "Functor.h"


#include <iostream>


using namespace std;





// Define a test function


void TestFunction(int i, double d)


{


   cout << "TestFunction(" << i


      << ", " << d << ") called." << endl;


}





int main()


{


   Functor<void, TYPELIST_2(int, double)> cmd(


      TestFunction);


   // will print: "TestFunction(4, 4.5) called."


   cmd(4, 4.5);


}


The explanation for this nice surprise comes from the workings of template parameter deduction. When the compiler sees a Functor constructed from TestFunction, it has no choice but to try the templated constructor. The compiler will then instantiate the template constructor with the template argument void (&)(int, double), which is the type of TestFunction. The constructor instantiates FunctorHandler<Functor<...>, void (&)(int, double)>. Consequently, the type of fun_ in FunctorHandler is void (&)(int, double) as well. When you invoke FunctorHandler<...>::operator(), it forwards to fun_(), which is legal syntax for invoking a function through a pointer to function. Thus, FunctorHandler supports pointers to functions out of the box because of two things: the syntactic similarity between pointers to functions and functors and the type inference mechanism that C++ uses.

There is a problem, though. (It couldn't be perfect, could it?) If you overload Test-Function—or any function that you pass to Functor<...>—you have to help a bit with some disambiguation. The reason is that if TestFunction is overloaded, the type of the symbol TestFunction is no longer defined. To illustrate this, let's add an overloaded version of TestFunction just before main:



// Declare an overloaded test function


// (no definition necessary)


void TestFunction(int);


Suddenly the compiler complains it cannot figure out which overload of TestFunction it should use. Because there are two functions called TestFunction, this name alone no longer suffices for identification.

In essence, in the presence of overloading, there are two ways to identify a specific function: by using initialization (or an assignment) and by using a cast. Let's illustrate both methods:



// as above, TestFunction is overloaded


int main()


{


   // Typedef used for convenience


   typedef void (*TpFun)(int, double);


   // Method 1: use an initialization


   TpFun pF = TestFunction;


   Functor<void, TYPELIST_2(int, double)>


   cmd1(4, 4.5);


   // Method 2: use a cast


   Functor<void, int, double> cmd2(


      static_cast<TpFun>(TestFunction)); // Ok


   cmd2(4, 4.5);


}


Both the initialization and the static cast let the compiler know that you're actually interested in the TestFunction that takes an int and a double and returns void.

    I l@ve RuBoard Previous Section Next Section