With three functions to work with, it could be handy to have an array of function pointers. Then one can use a for loop to call each function, via its pointer, in turn. What would that look like? Clearly, it should look something like the declaration of a single pointer, but there should be a [3] somewhere to indicate an array of three pointers. The question is where. And here’s the answer (including initialization):
const double * (*pa[3])(const double *, int) = {f1,f2,f3};
Why put the [3] there? Well, pa is an array of three things, and the starting point for declaring an array of three things is this: pa[3]. The rest of the declaration is about what kind of thing is to be placed in the array. Operator precedence ranks [] higher than *, so *pa[3] says pa is an array of three pointers. The rest of the declaration indicates what each pointer points to: a function with a signature of const double *, int and a return type of const double *. Hence, pa is an array of three pointers, each of which is a pointer to a function that takes a const double * and int as arguments and returns a const double *.
Can we use auto here? No. Automatic type deduction works with a single initializer value, not an initialization list. But now that we have the array pa, it is simple to declare a pointer of the matching type:
auto pb = pa;
The name of an array, as you’ll recall, is a pointer to its first element, so both pa and pb are pointers to a pointer to a function.
How can we use them to call a function? Both pa[i] and pb[i] represent pointers in the array, so you can use either of the function call notations with either of them:
const double * px = pa[0](av,3);
const double * py = (*pb[1])(av,3);
And you can apply the * operator to get the pointed-to double value:
double x = *pa[0](av,3);
double y = *(*pb[1])(av,3);
Something else you can do (and who wouldn’t want to?) is create a pointer to the whole array. Because the array name pa already is a pointer to a function pointer, a pointer to the array would be a pointer to a pointer to a pointer. This sounds intimidating, but because the result can be initialed with a single value, you can use auto:
auto pc = &pa // C++11 automatic type deduction
What if you prefer to do it yourself? Clearly, the declaration should resemble the declaration for pa, but because there is one more level of indirection, we’ll need one more * stuck somewhere. In particular, if we call the new pointer pd, we need to indicate that it is pointer, not an array name. This suggests the heart of the declaration should be (*pd)[3]. The parentheses bind the pd identifier to the *:
*pd[3] // an array of 3 pointers
(*pd)[3] // a pointer to an array of 3 elements
In other words, pd is a pointer, and it points to an array of three things. What those things are is described by the rest of the original declaration of pa. This approach yields the following:
const double *(*(*pd)[3])(const double *, int) = &pa
To call a function, realize that if pd points to an array, then *pd is the array and (*pd)[i] is an array element, which is a pointer to a function. The simpler notation, then, for the function call is (*pd)[i](av,3), and *(*pd)[i](av,3) would be the value that the returned pointer points to. Alternatively, you could use second syntax for invoking a function with a pointer and use (*(*pd)[i])(av,3) for the call and *(*(*pd)[i])(av,3) for the pointed-to double value.
Be aware of the difference between pa, which as an array name is an address, and &pa. As you’ve seen before, in most contexts pa is the address of the first element of the array—that is, &pa[0]. Therefore, it is the address of a single pointer. But &pa is the address of the entire array (that is, of a block of three pointers). Numerically, pa and &pa may have the same value, but they are of different types. One practical difference is that pa+1 is the address of the next element in the array, whereas &pa+1 is the address of the next block of 12 bytes (assuming addresses are 4 bytes) following the pa array. Another difference is that you dereference pa once to get the value of the first element and you deference &pa twice to get the same value:
**&pa == *pa == pa[0]
Listing 7.19 puts this discussion to use. For illustrative purposes, the functions f1(), and so on, have been kept embarrassingly simple. The program shows, as comments, the C++98 alternatives to using auto.
Listing 7.19. arfupt.cpp
// arfupt.cpp -- an array of function pointers
#include
// various notations, same signatures
const double * f1(const double ar[], int n);
const double * f2(const double [], int);
const double * f3(const double *, int);
int main()
{
using namespace std;
double av[3] = {1112.3, 1542.6, 2227.9};
// pointer to a function
const double *(*p1)(const double *, int) = f1;
auto p2 = f2; // C++11 automatic type deduction
// pre-C++11 can use the following code instead
// const double *(*p2)(const double *, int) = f2;
cout << "Using pointers to functions:\n";