19.4. Pointer to Class Member
A pointer to member is a pointer that can point to a nonstatic
member of a class. Normally a pointer points to an object, but a pointer to member identifies a member of a class, not an object of that class. static
class members are not part of any object, so no special syntax is needed to point to a static
member. Pointers to static
members are ordinary pointers.
The type of a pointer to member embodies both the type of a class and the type of a member of that class. We initialize such pointers to point to a specific member of a class without identifying an object to which that member belongs. When we use a pointer to member, we supply the object whose member we wish to use.
To explain pointers to members, we’ll use a version of the Screen
class from § 7.3.1 (p. 271):
class Screen {
public:
typedef std::string::size_type pos;
char get_cursor() const { return contents[cursor]; }
char get() const;
char get(pos ht, pos wd) const;
private:
std::string contents;
pos cursor;
pos height, width;
};
19.4.1. Pointers to Data Members
As with any pointer, we declare a pointer to member using a *
to indicate that the name we’re declaring is a pointer. Unlike ordinary pointers, a pointer to member also incorporates the class that contains the member. Hence, we must precede the *
with classname::
to indicate that the pointer we are defining can point to a member of classname. For example:
// pdata can point to a string member of a const (or non const) Screen object
const string Screen::*pdata;
declares that pdata
is a “pointer to a member of class Screen
that has type const string
.” The data members in a const
object are themselves const
. By making our pointer a pointer to const string
member, we say that we can use pdata
to point to a member of any Screen
object, const
or not. In exchange we can use pdata
to read, but not write to, the member to which it points.
When we initialize (or assign to) a pointer to member, we say to which member it points. For example, we can make pdata
point to the contents
member of an unspecified Screen
object as follows:
pdata = &Screen::contents;
Here, we apply the address-of operator not to an object in memory but to a member of the class Screen
.
Of course, under the new standard, the easiest way to declare a pointer to member is to use auto
or decltype
:
auto pdata = &Screen::contents;
Using a Pointer to Data Member
It is essential to understand that when we initialize or assign a pointer to member, that pointer does not yet point to any data. It identifies a specific member but not the object that contains that member. We supply the object when we dereference the pointer to member.
Analogous to the member access operators, .
and ->
, there are two pointer-to-member access operators, .*
and ->*
, that let us supply an object and dereference the pointer to fetch a member of that object:
Screen myScreen, *pScreen = &myScreen;
// .* dereferences pdata to fetch the contents member from the object myScreen
auto s = myScreen.*pdata;
// ->* dereferences pdata to fetch contents from the object to which pScreen points
s = pScreen->*pdata;
Conceptually, these operators perform two actions: They dereference the pointer to member to get the member that we want; then, like the member access operators, they fetch that member from an object (.*
) or through a pointer (->*
).
A Function Returning a Pointer to Data Member
Normal access controls apply to pointers to members. For example, the contents
member of Screen
is private
. As a result, the use of pdata
above must have been inside a member or friend of class Screen
or it would be an error.
Because data members are typically private
, we normally can’t get a pointer to data member directly. Instead, if a class like Screen
wanted to allow access to its contents
member, it would define a function to return a pointer to that member:
class Screen {
public:
// data is a static member that returns a pointer to member
static const std::string Screen::*data()
{ return &Screen::contents; }
// other members as before
};
Here we’ve added a static
member to class Screen
that returns a pointer to the contents
member of a Screen
. The return type of this function is the same type as our original pdata
pointer. Reading the return type from right to left, we see that data
returns a pointer to a member of class Screen
that is a string
that is const
. The body of the function applies the address-of operator to the contents
member, so the function returns a pointer to the contents
member of Screen
.
When we call data
, we get a pointer to member:
// data() returns a pointer to the contents member of class Screen
const string Screen::*pdata = Screen::data();
As before, pdata
points to a member of class Screen
but not to actual data. To use pdata
, we must bind it to an object of type Screen
// fetch the contents of the object named myScreen
auto s = myScreen.*pdata;
INFO
Exercises Section 19.4.1
Exercise 19.11: What is the difference between an ordinary data pointer and a pointer to a data member?
Exercise 19.12: Define a pointer to member that can point to the cursor
member of class Screen
. Fetch the value of Screen::cursor
through that pointer.
Exercise 19.13: Define the type that can represent a pointer to the bookNo
member of the Sales_data
class.
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.
INFO
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 typedef
s (§ 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
INFO
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,
};
INFO
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.
19.4.3. Using Member Functions as Callable Objects
As we’ve seen, to make a call through a pointer to member function, we must use the .*
or ->*
operators to bind the pointer to a specific object. As a result, unlike ordinary function pointers, a pointer to member is not a callable object; these pointers do not support the function-call operator (§ 10.3.2, p. 388).
Because a pointer to member is not a callable object, we cannot directly pass a pointer to a member function to an algorithm. As an example, if we wanted to find the first empty string
in a vector
of string
s, the obvious call won’t work:
auto fp = &string::empty; // fp points to the string empty function
// error: must use .* or ->* to call a pointer to member
find_if(svec.begin(), svec.end(), fp);
The find_if
algorithm expects a callable object, but we’ve supplied fp
, which is a pointer to a member function. This call won’t compile, because the code inside find_if
executes a statement something like
// check whether the given predicate applied to the current element yields true
if (fp(*it)) // error: must use ->* to call through a pointer to member
which attempts to call the object it was passed.
Using function
to Generate a Callable
One way to obtain a callable from a pointer to member function is by using the library function
template (§ 14.8.3, p. 577):
function<bool (const string&)> fcn = &string::empty;
find_if(svec.begin(), svec.end(), fcn);
Here we tell function
that empty
is a function that can be called with a string
and returns a bool
. Ordinarily, the object on which a member function executes is passed to the implicit this
parameter. When we want to use function
to generate a callable for a member function, we have to “translate” the code to make that implicit parameter explicit.
When a function
object holds a pointer to a member function, the function
class knows that it must use the appropriate pointer-to-member operator to make the call. That is, we can imagine that find_if
will have code something like
// assuming it is the iterator inside find_if, so *it is an object in the given range
if (fcn(*it)) // assuming fcn is the name of the callable inside find_if
which function
will execute using the proper pointer-to-member operator. In essence, the function
class will transform this call into something like
// assuming it is the iterator inside find_if, so *it is an object in the given range
if (((*it).*p)()) // assuming p is the pointer to member function inside fcn
When we define a function
object, we must specify the function type that is the signature of the callable objects that object can represent. When the callable is a member function, the signature’s first parameter must represent the (normally implicit) object on which the member will be run. The signature we give to function
must specify whether the object will be passed as a pointer or a reference.
When we defined fcn
, we knew that we wanted to call find_if
on a sequence of string
objects. Hence, we asked function
to generate a callable that took string
objects. Had our vector
held pointers to string
, we would have told function
to expect a pointer:
vector<string*> pvec;
function<bool (const string*)> fp = &string::empty;
// fp takes a pointer to string and uses the ->* to call empty
find_if(pvec.begin(), pvec.end(), fp);
Using mem_fn
to Generate a Callable
C++11To use function
, we must supply the call signature of the member we want to call. We can, instead, let the compiler deduce the member’s type by using another library facility, mem_fn
, which, like function
, is defined in the functional
header. Like function, mem_fn
generates a callable object from a pointer to member. Unlike function, mem_fn
will deduce the type of the callable from the type of the pointer to member:
find_if(svec.begin(), svec.end(), mem_fn(&string::empty));
Here we used mem_fn(&string::empty)
to generate a callable object that takes a string
argument and returns a bool
.
The callable generated by mem_fn
can be called on either an object or a pointer:
auto f = mem_fn(&string::empty); // f takes a string or a string*
f(*svec.begin()); // ok: passes a string object; f uses .* to call empty
f(&svec[0]); // ok: passes a pointer to string; f uses .-> to call empty
Effectively, we can think of mem_fn
as if it generates a callable with an overloaded function call operator—one that takes a string*
and the other a string&
.
Using bind
to Generate a Callable
For completeness, we can also use bind
(§ 10.3.4, p. 397) to generate a callable from a member function:
// bind each string in the range to the implicit first argument to empty
auto it = find_if(svec.begin(), svec.end(),
bind(&string::empty, _1));
As with function
, when we use bind
, we must make explicit the member function’s normally implicit parameter that represents the object on which the member function will operate. Like mem_fn
, the first argument to the callable generated by bind
can be either a pointer or a reference to a string
:
auto f = bind(&string::empty, _1);
f(*svec.begin()); // ok: argument is a string f will use .* to call empty
f(&svec[0]); // ok: argument is a pointer to string f will use .-> to call empty