Team LiB
Previous Section Next Section

6.7. Pointers to Functions

 

A function pointer is just that—a pointer that denotes a function rather than an object. Like any other pointer, a function pointer points to a particular type. A function’s type is determined by its return type and the types of its parameters. The function’s name is not part of its type. For example:

 

 

// compares lengths of two strings
bool lengthCompare(const string &, const string &);

 

has type bool(const string&, const string&). To declare a pointer that can point at this function, we declare a pointer in place of the function name:

 

 

// pf points to a function returning bool that takes two const string references
bool (*pf)(const string &, const string &);  // uninitialized

 

Starting from the name we are declaring, we see that pf is preceded by a *, so pf is a pointer. To the right is a parameter list, which means that pf points to a function. Looking left, we find that the type the function returns is bool. Thus, pf points to a function that has two const string& parameters and returns bool.

 

Image Note

The parentheses around *pf are necessary. If we omit the parentheses, then we declare pf as a function that returns a pointer to bool:

 

 

// declares a function named pf that returns a bool*
bool *pf(const string &, const string &);

 

 

Using Function Pointers

 

When we use the name of a function as a value, the function is automatically converted to a pointer. For example, we can assign the address of lengthCompare to pf as follows:

 

 

pf = lengthCompare;  // pf now points to the function named lengthCompare
pf = &lengthCompare; // equivalent assignment: address-of operator is optional

 

Moreover, we can use a pointer to a function to call the function to which the pointer points. We can do so directly—there is no need to dereference the pointer:

 

 

bool b1 = pf("hello", "goodbye");    // calls lengthCompare
bool b2 = (*pf)("hello", "goodbye"); // equivalent call
bool b3 = lengthCompare("hello", "goodbye"); // equivalent call

 

There is no conversion between pointers to one function type and pointers to another function type. However, as usual, we can assign nullptr2.3.2, p. 53) or a zero-valued integer constant expression to a function pointer to indicate that the pointer does not point to any function:

 

 

string::size_type sumLength(const string&, const string&);
bool cstringCompare(const char*, const char*);
pf = 0;              // ok: pf points to no function
pf = sumLength;      // error: return type differs
pf = cstringCompare; // error: parameter types differ
pf = lengthCompare;  // ok: function and pointer types match exactly

 

Pointers to Overloaded Functions

 

As usual, when we use an overloaded function, the context must make it clear which version is being used. When we declare a pointer to an overloaded function

 

 

void ff(int*);
void ff(unsigned int);
void (*pf1)(unsigned int) = ff;  // pf1 points to ff(unsigned)

 

the compiler uses the type of the pointer to determine which overloaded function to use. The type of the pointer must match one of the overloaded functions exactly:

 

 

void (*pf2)(int) = ff;    // error: no ff with a matching parameter list
double (*pf3)(int*) = ff; // error: return type of ff and pf3 don't match

 

Function Pointer Parameters

 

Just as with arrays (§ 6.2.4, p. 214), we cannot define parameters of function type but can have a parameter that is a pointer to function. As with arrays, we can write a parameter that looks like a function type, but it will be treated as a pointer:

 

 

// third parameter is a function type and is automatically treated as a pointer to function
void useBigger(const string &s1, const string &s2,
               bool pf(const string &, const string &));
// equivalent declaration: explicitly define the parameter as a pointer to function
void useBigger(const string &s1, const string &s2,
               bool (*pf)(const string &, const string &));

 

When we pass a function as an argument, we can do so directly. It will be automatically converted to a pointer:

 

 

// automatically converts the function lengthCompare to a pointer to function
useBigger(s1, s2, lengthCompare);

 

As we’ve just seen in the declaration of useBigger, writing function pointer types quickly gets tedious. Type aliases (§ 2.5.1, p. 67), along with decltype2.5.3, p. 70), let us simplify code that uses function pointers:

 

 

// Func and Func2 have function type
typedef bool Func(const string&, const string&);
typedef decltype(lengthCompare) Func2; // equivalent type

// FuncP and FuncP2 have pointer to function type
typedef bool(*FuncP)(const string&, const string&);
typedef decltype(lengthCompare) *FuncP2;  // equivalent type

 

Here we’ve used typedef to define our types. Both Func and Func2 are function types, whereas FuncP and FuncP2 are pointer types. It is important to note that decltype returns the function type; the automatic conversion to pointer is not done. Because decltype returns a function type, if we want a pointer we must add the * ourselves. We can redeclare useBigger using any of these types:

 

 

// equivalent declarations of useBigger using type aliases
void useBigger(const string&, const string&, Func);
void useBigger(const string&, const string&, FuncP2);

 

Both declarations declare the same function. In the first case, the compiler will automatically convert the function type represented by Func to a pointer.

 

Returning a Pointer to Function

 

As with arrays (§ 6.3.3, p. 228), we can’t return a function type but can return a pointer to a function type. Similarly, we must write the return type as a pointer type; the compiler will not automatically treat a function return type as the corresponding pointer type. Also as with array returns, by far the easiest way to declare a function that returns a pointer to function is by using a type alias:

 

 

using F = int(int*, int);     // F is a function type, not a pointer
using PF = int(*)(int*, int); // PF is a pointer type

 

Here we used type alias declarations (§ 2.5.1, p. 68) to define F as a function type and PF as a pointer to function type. The thing to keep in mind is that, unlike what happens to parameters that have function type, the return type is not automatically converted to a pointer type. We must explicitly specify that the return type is a pointer type:

 

 

PF f1(int); // ok: PF is a pointer to function; f1 returns a pointer to function
F f1(int);  // error: F is a function type; f1 can't return a function
F *f1(int); // ok: explicitly specify that the return type is a pointer to function

 

Of course, we can also declare f1 directly, which we’d do as

 

int (*f1(int))(int*, int);

 

Reading this declaration from the inside out, we see that f1 has a parameter list, so f1 is a function. f1 is preceded by a * so f1 returns a pointer. The type of that pointer itself has a parameter list, so the pointer points to a function. That function returns an int.

 

For completeness, it’s worth noting that we can simplify declarations of functions that return pointers to function by using a trailing return (§ 6.3.3, p. 229):

 

 

auto f1(int) -> int (*)(int*, int);

 

Using auto or decltype for Function Pointer Types

 

If we know which function(s) we want to return, we can use decltype to simplify writing a function pointer return type. For example, assume we have two functions, both of which return a string::size_type and have two const string& parameters. We can write a third function that takes a string parameter and returns a pointer to one of these two functions as follows:

 

 

string::size_type sumLength(const string&, const string&);
string::size_type largerLength(const string&, const string&);

// depending on the value of its string parameter,
// getFcn returns a pointer to sumLength or to largerLength
decltype(sumLength) *getFcn(const string &);

 

The only tricky part in declaring getFcn is to remember that when we apply decltype to a function, it returns a function type, not a pointer to function type. We must add a * to indicate that we are returning a pointer, not a function.

 

Exercises Section 6.7

 

Exercise 6.54: Write a declaration for a function that takes two int parameters and returns an int, and declare a vector whose elements have this function pointer type.

Exercise 6.55: Write four functions that add, subtract, multiply, and divide two int values. Store pointers to these values in your vector from the previous exercise.

Exercise 6.56: Call each element in the vector and print their result.


 
Team LiB
Previous Section Next Section