Concepts / Function Objects and Function Adaptors

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.

  • Programming

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?

  • It calls draw on the object that p points to

  • It creates a new Shape object and then calls draw on it

  • It calls a draw function that does not need any object

Reveal answer
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

Definition

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.

Key point

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.

mem_fnadaptor that wraps themember function pointer&address-of: produces apointer to the memberShapeclass scope that owns themember::scope operator joiningclass and namedrawmember function nameargument isapplies tofollowed bynames
In mem_fn(&Shape::draw), the adaptor is mem_fn, the class scope is Shape, the member name is draw, and the leading & produces a pointer to the member function.

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.

Notes on the code
  • 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
f(p)call to the function objectpobject pointer argumentobject p points tothe Shape objectShape::drawmember function body runson that objectfirst argumentdereferenced toacted on by
When the function object is called as f(p), the pointer p is supplied as the object argument, and the member function draw then runs on the object p points to.
Worked example

Tracing f(p) step by step

Given the Shape code above, what happens when the line f(p) runs?

  1. 1
    Identify the callee

    f is the function object that mem_fn(&Shape::draw) created. It is not the member function itself.

  2. 2
    Read the first argument

    The argument p is an object pointer of the matching class Shape. It supplies the object that the call acts on.

  3. 3
    Run the member function

    The member function draw runs on the object that p points to, the same object that p->draw() would have used.

Result

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.

nonmember callg(x)object needednomember via mem_fnf(p)object neededyes, passed as firstargument
A nonmember function is called with its arguments alone, while a member function reached through mem_fn is called with the object pointer as its first argument.
FeaturePlain member callCall through mem_fn object
Form of the callp->draw()f(p)
Object supplied byThe pointer used with ->The first argument of the call
Created fromNot applicableThe member function pointer &Shape::draw
Callable like a nonmember functionNoYes

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 like
    f(); instead of f(p);
    Why it fails

    The member function needs an object to act on, and the function object receives that object as its first argument.

    Do this instead

    Pass an object pointer of the matching class as the first argument.

  • Common mistake

    Dropping the address-of operator

    What it looks like
    mem_fn(Shape::draw) instead of mem_fn(&Shape::draw)
    Why it fails

    The & marks the name as a pointer to the member function. Without it, the expression does not form the member function pointer that mem_fn expects.

    Do this instead

    Keep the & in front of the class-and-name combination.

  • Common mistake

    Treating the function object as the member function itself

    What it looks like

    Expecting f to run draw with no argument, as if f were p->draw()

    Why it fails

    mem_fn returns 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 instead

    Create 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

Best practice

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

EASY

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_fn needs 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_fn is the adaptor, Class is the scope, member_function is the member name, and & produces a pointer to the member function.
  • A function object created by mem_fn is 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.

Related concepts

More concepts starting with F