19.4.2. Pointers to Member Functions

We can also define a pointer that can point to a member function of a class. As with pointers to data members, the easiest way to form a pointer to member function is to use auto to deduce the type for us:

// pmf is a pointer that can point to a Screen member function that is const
// that returns a char and takes no arguments
auto pmf = &Screen::get_cursor;

Like a pointer to data member, a pointer to a function member is declared using classname::*. Like any other function pointer (§ 6.7, p. 247), a pointer to member function specifies the return type and parameter list of the type of function to which this pointer can point. If the member function is a const member (§ 7.1.2, p. 258) or a reference member (§ 13.6.3, p. 546), we must include the const or reference qualifier as well.

As with normal function pointers, if the member is overloaded, we must distinguish which function we want by declaring the type explicitly (§ 6.7, p. 248). For example, we can declare a pointer to the two-parameter version of get as

char (Screen::*pmf2)(Screen::pos, Screen::pos) const;
pmf2 = &Screen::get;

The parentheses around Screen::* in this declaration are essential due to precedence. Without the parentheses, the compiler treats the following as an (invalid) function declaration:

// error: nonmember function p cannot have a const qualifier
char Screen::*p(Screen::pos, Screen::pos) const;

This declaration tries to define an ordinary function named p that returns a pointer to a member of class Screen that has type char. Because it declares an ordinary function, the declaration can’t be followed by a const qualifier.

Unlike ordinary function pointers, there is no automatic conversion between a member function and a pointer to that member:

// pmf points to a Screen member that takes no arguments and returns char
pmf = &Screen::get; // must explicitly use the address-of operator
pmf = Screen::get;  // error: no conversion to pointer for member functions

Using a Pointer to Member Function

As when we use a pointer to a data member, we use the .* or ->* operators to call a member function through a pointer to member:

Screen myScreen,*pScreen = &myScreen;
// call the function to which pmf points on the object to which pScreen points
char c1 = (pScreen->*pmf)();
// passes the arguments 0, 0 to the two-parameter version of get on the object myScreen
char c2 = (myScreen.*pmf2)(0, 0);

The calls (myScreen->*pmf)() and (pScreen.*pmf2)(0,0) require the parentheses because the precedence of the call operator is higher than the precedence of the pointer to member operators.

Without the parentheses,

myScreen.*pmf()

would be interpreted to mean

myScreen.*(pmf())

This code says to call the function named pmf and use its return value as the operand of the pointer-to-member operator (.*). However, pmf is not a function, so this code is in error.


Image Note

Because of the relative precedence of the call operator, declarations of pointers to member functions and calls through such pointers must use parentheses: (C::*p)(parms) and (obj.*p)(args).


Using Type Aliases for Member Pointers

Type aliases or typedefs (§ 2.5.1, p. 67) make pointers to members considerably easier to read. For example, the following type alias defines Action as an alternative name for the type of the two-parameter version of get:

// Action is a type that can point to a member function of Screen
// that returns a char and takes two pos arguments
using Action =
char (Screen::*)(Screen::pos, Screen::pos) const;

Action is another name for the type “pointer to a const member function of class Screen taking two parameters of type pos and returning char.” Using this alias, we can simplify the definition of a pointer to get as follows:

Action get = &Screen::get; // get points to the get member of Screen

As with any other function pointer, we can use a pointer-to-member function type as the return type or as a parameter type in a function. Like any other parameter, a pointer-to-member parameter can have a default argument:

// action takes a reference to a Screen and a pointer to a Screen member function
Screen& action(Screen&, Action = &Screen::get);

action is a function taking two parameters, which are a reference to a Screen object and a pointer to a member function of class Screen that takes two pos parameters and returns a char. We can call action by passing it either a pointer or the address of an appropriate member function in Screen:

Screen myScreen;
// equivalent calls:
action(myScreen);      // uses the default argument
action(myScreen, get); // uses the variable get that we previously defined
action(myScreen, &Screen::get); // passes the address explicitly


Image Note

Type aliases make code that uses pointers to members much easier to read and write.


Pointer-to-Member Function Tables

One common use for function pointers and for pointers to member functions is to store them in a function table (§ 14.8.3, p. 577). For a class that has several members of the same type, such a table can be used to select one from the set of these members. Let’s assume that our Screen class is extended to contain several member functions, each of which moves the cursor in a particular direction:

class Screen {
public:
    // other interface and implementation members as before
    Screen& home();     // cursor movement functions
    Screen& forward();
    Screen& back();
    Screen& up();
    Screen& down();
};

Each of these new functions takes no parameters and returns a reference to the Screen on which it was invoked.

We might want to define a move function that can call any one of these functions and perform the indicated action. To support this new function, we’ll add a static member to Screen that will be an array of pointers to the cursor movement functions:

class Screen {
public:
    // other interface and implementation members as before
    // Action is a pointer that can be assigned any of the cursor movement members
    using Action = Screen& (Screen::*)();
    // specify which direction to move; enum see § 19.3 (p. 832)
    enum Directions { HOME, FORWARD, BACK, UP, DOWN };
    Screen& move(Directions);
private:
    static Action Menu[];      // function table
};

The array named Menu will hold pointers to each of the cursor movement functions. Those functions will be stored at the offsets corresponding to the enumerators in Directions. The move function takes an enumerator and calls the appropriate function:

Screen& Screen::move(Directions cm)
{
    // run the element indexed by cm on this object
    return (this->*Menu[cm])(); // Menu[cm] points to a member function
}

The call inside move is evaluated as follows: The Menu element indexed by cm is fetched. That element is a pointer to a member function of the Screen class. We call the member function to which that element points on behalf of the object to which this points.

When we call move, we pass it an enumerator that indicates which direction to move the cursor:

Screen myScreen;
myScreen.move(Screen::HOME);  // invokes myScreen.home
myScreen.move(Screen::DOWN);  // invokes myScreen.down

What’s left is to define and initialize the table itself:

Screen::Action Screen::Menu[] = { &Screen::home,
                                  &Screen::forward,
                                  &Screen::back,
                                  &Screen::up,
                                  &Screen::down,
                                };


Exercises Section 19.4.2

Exercise 19.14: Is the following code legal? If so, what does it do? If not, why?

auto pmf = &Screen::get_cursor;
pmf = &Screen::get;

Exercise 19.15: What is the difference between an ordinary function pointer and a pointer to a member function?

Exercise 19.16: Write a type alias that is a synonym for a pointer that can point to the avg_price member of Sales_data.

Exercise 19.17: Define a type alias for each distinct Screen member function type.


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.183.10