Pointers to Member Functions
The function adaptor mem_fn(mf) produces a function object that can be called as a nonmember function.
Why a Member Function Needs an Object
A member function normally needs an object to act on. When you write p->draw(), the pointer p tells the program which object to use, and then draw runs on that object. The member function is never meaningful on its own; it is always attached to some object of its class.
A member function pointer such as &Shape::draw names the function without naming any particular object. It identifies draw as a member of the Shape class, but it does not say which Shape it should work on. That gap is the central idea of this article: the pointer describes the function, and something else must supply the object at the moment of the call.
What mem_fn() Produces
The function adaptor mem_fn(mf) takes a member function pointer mf and produces a function object that can be called as a nonmember function.
mem_fn() takes the member function pointer and wraps it in a function object. The result can be called like an ordinary function, and the object pointer is passed in as a regular argument. Instead of writing the object first and then a member access, you write the object pointer as the first argument of a function call. This is why the source describes the result as callable as a nonmember function.
The usefulness of this wrapping is that a member function becomes a value you can store in a variable and pass around, just like any other callable. A function object that already knows which member function it calls can be handed to code that only knows how to call things with arguments.
Reading mem_fn(&Shape::draw) Piece by Piece
The pattern to remember is mem_fn(&Class::member_function). The class name appears before the member function name, joined by the scope operator. The address-of operator in front marks the whole thing as a pointer to the member function. The outer call to mem_fn() wraps that pointer into a function object.
The expression as a whole produces a function object, not a call. Nothing runs when mem_fn(&Shape::draw) is evaluated. The function object is created and is ready to be called later with an object pointer. The variable that holds it can have any name. The source uses draw, which matches the member function name, but a name like render would work just as well.
Tracing a Direct Call Next to a Function Object Call
The following example makes the same member function call twice, first directly through a pointer and then through a function object created by mem_fn(). Read the code once, then focus on the line where the function object is created and the line where it is called.
drawing
drawing- p->
draw()names the object through p and then runs draw on it - std::
mem_fn(&Shape::draw)creates the function object; nothing is called yet draw(p)calls the function object and passes p as the object argument
What do you think happens?
Before reading the output, what does the line draw(p) cause the program to do?
Reveal answer
It calls draw on the object that p points to
The function object was built from the member function pointer, and the pointer p is supplied as the ordinary argument. The call therefore runs Shape::draw on the object p points to, so the program prints drawing a second time.
drawing
drawingHow the Function Object Is Called
The function object splits the work into two parts. The member function pointer was fixed when the object was created, and it is stored inside the function object. The object the member function should act on is supplied only at call time, as the first argument. When draw(p) runs, the function object takes p, uses it to find the object, and performs the member function call on that object.
Read the diagram from left to right. The caller writes draw(p) with the object pointer as an ordinary argument. The function object does not hold any particular object. It holds only the member function pointer, and at the moment of the call it uses p as the object on which that member function acts.
Worked Example: Building and Calling a Function Object
Creating and calling a function object for a Lamp class
A class Lamp has a member function toggle(). Create a function object that calls toggle, then call it on a Lamp pointer named lamp_ptr.
- 1Identify the member function pointer
The member function is Lamp::toggle, so the pointer is &Lamp::toggle. The class name comes first, joined to the member name by the scope operator, and the address-of operator marks it as a pointer.
- 2Create the function object
Pass the pointer to
mem_fn(), giving autotoggle_fn= std::mem_fn(&Lamp::toggle);. This line only builds the function object. It does not run toggle. - 3Supply the object at call time
Call the function object with the pointer as its first argument:
toggle_fn(lamp_ptr);. The member function now runs on the objectlamp_ptrpoints to. - 4Check the three places in order
If the call fails, first confirm that toggle is a real member of Lamp, then that the function object was created and stored, and finally that the argument is a pointer to a Lamp.
toggle_fn(lamp_ptr) runs Lamp::toggle on the object pointed to by lamp_ptr. The function object stores the member pointer, and the caller supplies the object.
Mistakes That Break mem_fn() Calls
Common mistake Dropping the address-of operator
What it looks likeauto draw = std::mem_fn(Shape::draw);Why it failsThe argument to
mem_fn()must be a member function pointer, written with the address-of operator as &Shape::draw. Without it, the expression does not name the member function pointer the adaptor expects.Do this insteadauto draw = std::mem_fn(&Shape::draw);Common mistake Omitting the object argument
What it looks likedraw();Why it failsThe function object needs an object to act on. The member function pointer does not supply one, so the call has no object to work with.
Do this insteaddraw(p); where p is a pointer to a ShapeCommon mistake Treating the function object as the member function itself
What it looks likep->draw_fn();Why it failsThe function object is a separate value that wraps the member function pointer. It is called with an object as its argument, not used with the member access syntax of the function it wraps.
Do this insteaddraw_fn(p);Common mistake Passing an object of the wrong class
Why it failsThe member function pointer belongs to a specific class. The object supplied at call time must match that class, or the call does not make sense for the member being invoked.
Do this insteadMake sure the pointer passed in points to an object of the class named in &Class::member_function.
Common mistake Creating the function object without storing it, then looking for it later in the code
A failure at the call site often has its cause in the creation line. Because the function object is built once and called later, the mistake may sit in the pointer on the right side of mem_fn() even though the error appears where draw(p) is written. Trace back through the three places before changing the call itself.
Practice: Tracing a Broken Call
A class Door has a member function open(). A programmer writes auto open_fn = std::mem_fn(Door::open); and later calls open_fn(d_ptr); where d_ptr is a pointer to a Door. Identify the mistake, state where the failure first appears, and rewrite the creation line.
Hints
- Compare the argument to
mem_fn()with the patternmem_fn(&Class::member_function). - The error appears at the call, but the cause is in the creation line.
- The fix changes only the argument to
mem_fn(), not the call.
Key Takeaways
- The function adaptor
mem_fn(mf)takes a member function pointer and produces a function object that can be called as a nonmember function. - The pattern
mem_fn(&Class::member_function)names the class, the member, and the address-of marker; the whole expression creates a function object without calling anything. - The function object stores the member function pointer, and the object pointer is supplied as the first argument when the function object is called.
- The common mistakes are dropping the address-of operator, omitting the object argument, treating the function object as the member function itself, and passing an object of the wrong class.
- When a call fails, check the member pointer, the stored function object, and the argument in that order, because the cause often lies in the creation line.