Lesson 8 | Signature-matching algorithm |
Objective | Describe the two parts of the signature-matching algorithm. |
C++ Signature-matching Algorithm
There are two parts to the signature-matching algorithm.
- The first part determines a best match for each argument.
- The second part sees if there is one function that is a uniquely best match in each argument.
This uniquely best match is defined as being a best match on at least one argument, and a "tied-for-best" match on all other arguments.
For a given argument, a best match is always an exact match. The algorithm also considers whether an argument has been declared as const.
The following can be unambiguously overloaded.
void print(int i);
void print(const int& i);
Declaring a Pointer to a Function
To declare pointers to a data type, the declaration has had to specify exactly to what type the pointer points. Similarly, a pointer to a function has to specify to what type of function the pointer points.This means the declaration should identify the function's return type and the function's signature (its argument list).That is, the declaration should provide the same information about a function that a function prototype does. For example, suppose Pam LeCoder has written a time-estimating function with the following prototype:
double pam(int); // prototype
Here is what a declaration of an appropriate pointer type looks like:
double (*pf)(int); // pf points to a function that takes
// one int argument and that
// returns type double
Tip
In general, to declare a pointer to a particular kind of function, you can first write a prototype for a regular function of the desired kind and then replace the function name with an expression in the form (*pf). In this case, pf is a pointer to a function of that type. The declaration requires the parentheses around *pf to provide the proper operator precedence. Parentheses have a higher precedence than the * operator, so *pf(int) means pf() is a function that returns a pointer, whereas (*pf)(int) means pf is a pointer to a function:
double (*pf)(int); // pf points to a function that returns double
double *pf(int); // pf() a function that returns a pointer-to-double
After you declare pf properly, you can assign to it the address of a matching function:
double pam(int);
double (*pf)(int);
pf = pam; // pf now points to the pam() function
Note that pam() has to match pf in both signature and return type.The compiler rejects nonmatching assignments:
double ned(double);
int ted(int);
double (*pf)(int);
pf = ned; // invalid -- mismatched signature
pf = ted; // invalid -- mismatched return types
Let us return to the estimate() function mentioned earlier. Suppose you want to pass to it the number of lines of code to be written and the address of an estimating algorithm, such as the pam() function. It could have the following prototype:
void estimate(int lines, double (*pf)(int));
This declaration says the second argument is a pointer to a function that has an int argument and a double return value.To have estimate() use the pam() function, you
pass the address of pam() to it:
estimate(50, pam); // function call telling estimate() to use pam()
Clearly, the tricky part about using pointers to functions is writing the prototypes, whereas passing the address is very simple.