Function Objects and Function Adaptors
mem_fn does not call the member function. It returns a function object. The member function runs only when that function object is called, and at that point an object pointer must be supplied as an argument.
Two Ways to Call the Same Member Function
Calling a member function usually looks like p->draw(), where p is a pointer to an object and draw is a member function of that object's class. The pointer tells the call which object to act on. This article introduces a second way to make the same call: wrap the member function in a function object and then call that function object like an ordinary function. The function adaptor mem_fn makes that wrapping possible.
Keep both forms in mind as you read. The first form calls the member function directly through a pointer. The second form creates a function object with mem_fn() and then calls that object. The goal of this article is to explain what the second form creates, how each part of its creation line is read, and where it tends to go wrong.
Tracing a Direct Member Call
A member function normally needs an object to act on. In p->draw(), the pointer p is what identifies the object, and the call runs draw on that object. The member function itself does not carry an object with it. It needs one to be supplied at the call site.
What do you think happens?
Given a pointer p to a Shape object, what does the expression p->draw() do?
Reveal answer
It calls draw on the object that p points to
The pointer p supplies the object. The member function runs on that object, so the object is found through the pointer at the call.
What mem_fn Produces
The function adaptor mem_fn(mf) takes a member function pointer mf and produces a function object. That function object can be called as a nonmember function, which means you call it with ordinary function-call syntax and pass the object as an argument.
The member function pointer names the function without naming any particular object. The adaptor takes that unbound name and wraps it. Once wrapped, the function object can be stored in a variable, passed around, and called like any other function object, which makes member functions usable in places that expect something callable.
mem_fn does not call the member function. It returns a function object. The member function runs only when that function object is called, and at that point an object pointer must be supplied as an argument.
Reading mem_fn(&Shape::draw)
The pattern to remember is mem_fn(&Class::member_function). The class name appears first, joined to the member function name by the scope operator. The address-of operator in front marks the whole class-and-name combination as a pointer to the member function. The adaptor mem_fn then wraps that pointer.
The source names the resulting function object draw, which matches the member function name. That is only a naming choice. The variable holding the function object could have any name, and the name draw does not make it a member function.
Calling the Function Object With an Object Pointer
The following illustrative example uses a small Shape class. It shows the direct call and the function-object call side by side. The example is generated for teaching and is not taken from the source. Read the code once, then find the line where the function object is created and the line where it is called.
- p->
draw()finds the object through the pointer p and runs draw on it mem_fn(&Shape::draw)creates a function object from the member function pointer- f(p) calls that function object and supplies p as the object argument
Tracing f(p) step by step
Given the Shape code above, what happens when the line f(p) runs?
- 1Identify the callee
f is the function object that
mem_fn(&Shape::draw)created. It is not the member function itself. - 2Read the first argument
The argument p is an object pointer of the matching class Shape. It supplies the object that the call acts on.
- 3Run the member function
The member function draw runs on the object that p points to, the same object that p->
draw()would have used.
f(p) performs the same member function call as p->draw(), but through a function object.
Member Pointers Versus Nonmember Pointers
A nonmember function needs only its arguments. A member function needs an object as well. That is the reason the member function pointer produced by &Class::member_function cannot be called by itself: it names a function that must be run on some object. mem_fn bridges this gap by letting the object be passed as an ordinary first argument, so the resulting function object can be called the same way a nonmember function is called.
| Feature | Plain member call | Call through mem_fn object |
|---|---|---|
| Form of the call | p->draw() | f(p) |
| Object supplied by | The pointer used with -> | The first argument of the call |
| Created from | Not applicable | The member function pointer &Shape::draw |
| Callable like a nonmember function | No | Yes |
Two ways to reach the same member function
Mistakes That Break mem_fn Calls
Most failures with mem_fn come from three recognizable errors. Each one either omits a part of the pattern or confuses the function object with the member function it wraps.
Common mistake Omitting the object argument
What it looks likef(); instead of f(p);Why it failsThe member function needs an object to act on, and the function object receives that object as its first argument.
Do this insteadPass an object pointer of the matching class as the first argument.
Common mistake Dropping the address-of operator
What it looks likemem_fn(Shape::draw) instead of mem_fn(&Shape::draw)Why it failsThe & marks the name as a pointer to the member function. Without it, the expression does not form the member function pointer that
mem_fnexpects.Do this insteadKeep the & in front of the class-and-name combination.
Common mistake Treating the function object as the member function itself
What it looks likeExpecting f to run draw with no argument, as if f were p->draw()
Why it failsmem_fnreturns a function object. It does not run the member function when created, and the function object still needs an object pointer when it is called.Do this insteadCreate the function object once with mem_fn, then call it with an object pointer.
Common mistake Using a member function pointer from a class that does not match the object pointer passed in
When a mem_fn call fails, check the three places in the pattern in order. First, confirm that the member function pointer on the right side of mem_fn names a real member of the class. Second, confirm that the function object is created from that pointer and stored in a variable. Third, confirm that the call passes an object pointer of the matching class. The error often appears at the call even when the mistake is in the creation line, so check creation before blaming the call.
Checking a Failing Call
A learner writes auto g = mem_fn(&Shape::draw); and later calls g(); with no argument. Using the three checks from this article, explain where the failure appears and what the corrected call should look like.
Hints
- Look at the first check: the member function pointer is correct, so focus on the call.
- A function object created by
mem_fnneeds an object pointer as its first argument. - The corrected call is g(p) when p is a pointer to a Shape object.
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. - In
mem_fn(&Class::member_function),mem_fnis the adaptor, Class is the scope,member_functionis the member name, and & produces a pointer to the member function. - A function object created by
mem_fnis called with an object pointer as its first argument, which the member function then acts on. - Common mistakes include omitting the object argument, dropping the address-of operator, and treating the function object as the member function itself.
- When a call fails, check the member pointer, the function object creation, and the object pointer argument in that order.