cout << " Address Value\n";
cout << (*p1)(av,3) << ": " << *(*p1)(av,3) << endl;
cout << p2(av,3) << ": " << *p2(av,3) << endl;
// pa an array of pointers
// auto doesn't work with list initialization
const double *(*pa[3])(const double *, int) = {f1,f2,f3};
// but it does work for initializing to a single value
// pb a pointer to first element of pa
auto pb = pa;
// pre-C++11 can use the following code instead
// const double *(**pb)(const double *, int) = pa;
cout << "\nUsing an array of pointers to functions:\n";
cout << " Address Value\n";
for (int i = 0; i < 3; i++)
cout << pa[i](av,3) << ": " << *pa[i](av,3) << endl;
cout << "\nUsing a pointer to a pointer to a function:\n";
cout << " Address Value\n";
for (int i = 0; i < 3; i++)
cout << pb[i](av,3) << ": " << *pb[i](av,3) << endl;
// what about a pointer to an array of function pointers
cout << "\nUsing pointers to an array of pointers:\n";
cout << " Address Value\n";
// easy way to declare pc
auto pc = &pa
// pre-C++11 can use the following code instead
// const double *(*(*pc)[3])(const double *, int) = &pa
cout << (*pc)[0](av,3) << ": " << *(*pc)[0](av,3) << endl;
// hard way to declare pd
const double *(*(*pd)[3])(const double *, int) = &pa
// store return value in pdb
const double * pdb = (*pd)[1](av,3);
cout << pdb << ": " << *pdb << endl;
// alternative notation
cout << (*(*pd)[2])(av,3) << ": " << *(*(*pd)[2])(av,3) << endl;
// cin.get();
return 0;
}
// some rather dull functions
const double * f1(const double * ar, int n)
{
return ar;
}
const double * f2(const double ar[], int n)
{
return ar+1;
}
const double * f3(const double ar[], int n)
{
return ar+2;
}
And here is the output:
Using pointers to functions:
Address Value
002AF9E0: 1112.3
002AF9E8: 1542.6
Using an array of pointers to functions:
Address Value
002AF9E0: 1112.3
002AF9E8: 1542.6
002AF9F0: 2227.9
Using a pointer to a pointer to a function:
Address Value
002AF9E0: 1112.3
002AF9E8: 1542.6
002AF9F0: 2227.9
Using pointers to an array of pointers:
Address Value
002AF9E0: 1112.3
002AF9E8: 1542.6
002AF9F0: 2227.9
The addresses shown are the locations of the double values in the av array.
This example may seem esoteric, but pointers to arrays of pointers to functions are not unheard of. Indeed, the usual implementation of virtual class methods (see Chapter 13, “Class Inheritance”) uses this technique. Fortunately, the compiler handles the details.
Appreciating auto
One of the goals of C++11 is to make C++ easier to use, letting the programmer concentrate more on design and less on details. Listing 7.19 surely illustrates this point:
auto pc = &pa // C++11 automatic type deduction
const double *(*(*pd)[3])(const double *, int) = &pa // C++98, do it yourself
The automatic type deduction feature reflects a philosophical shift in the role of the compiler. In C++98, the compiler uses its knowledge to tell you when you are wrong. In C++11, at least with this feature, it uses its knowledge to help you get the right declaration.
There is a potential drawback. Automatic type deduction ensures that the type of the variable matches the type of the initializer, but it still is possible that you might provide the wrong type of initializer:
auto pc = *pa; // oops! used *pa instead of &pa
This declaration would make pc match the type of *pa, and that would result in a compile-time error when Listing 7.19 later uses pc, assuming that it is of the same type as &pa.
Simplifying with typedef
C++ does provide tools other than auto for simplifying declarations. You may recall from Chapter 5, “Loops and Relational Expressions,” that the typedef keyword allows you to create a type alias:
typedef double real; // makes real another name for double
The technique is to declare the alias as if it were an identifier and to insert the keyword typedef at the beginning. So you can do this to make p_fun an alias for the function pointer type used in Listing 7.19:
typedef const double *(*p_fun)(const double *, int); // p_fun now a type name
p_fun p1 = f1; // p1 points to the f1() function
You then can use this type to build elaborations:
p_fun pa[3] = {f1,f2,f3}; // pa an array of 3 function pointers
p_fun (*pd)[3] = &pa // pd points to an array of 3 function pointers
Not only does typedef save you some typing, it makes writing the code less error prone, and it makes the program easier to understand.
Summary
Functions are the C++ programming modules. To use a function, you need to provide a definition and a prototype, and you have to use a function call. The function definition is the code that implements what the function does. The function prototype describes the function interface: how many and what kinds of values to pass to the function and what sort of return type, if any, to get from it. The function call causes the program to pass the function arguments to the function and to transfer program execution to the function code.