Member Functions and the this Pointer
The function adaptor mem_fn(mf) produces a function object that can be called as a nonmember function.
Two Calls That Look Alike
This article begins with code rather than definitions. The program below does two things that look similar. It first calls a member function directly through a pointer, and then it calls the same member function through a function object created by mem_fn(). Read the code once, then pay attention to 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 - std::
mem_fn(&Shape::draw)takes the member function pointer and wraps it in a function object draw(p)calls the function object, and the object pointer is passed as an ordinary argument
What do you think happens?
Do the two calls, p->draw() and draw(p), end up running the same member function on the same object?
Reveal answer
Yes, both run draw on the object that p points to.
The function object is just another way to make the same member function call. The difference is that the object is passed in as a plain argument instead of being written before the arrow.
Why a Member Function Needs an Object
A member function normally needs an object to act on. In the code above, p->draw() uses the pointer p to find the object, and then runs draw on that object. The member function pointer &Shape::draw is different: it names the function without naming any particular object. It tells you which member function to use, but not which Shape it should act on.
This gap is the reason mem_fn() exists. A member function pointer by itself cannot be called the way a plain function is called, because the object has to be supplied somewhere. The adaptor fills that gap by wrapping the pointer in something that accepts the object as an ordinary argument.
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, with the object pointer supplied as its first argument.
The function object is the key result. It is a value you can store in a variable, such as draw in the earlier program, and then call later. The member function pointer on its own is not used that way. The comparison below shows the difference between the two forms.
Reading the mem_fn Expression
The syntax form mem_fn(&Class::member_function) is the pattern to remember. Read it from the inside out. The name of the class 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 adaptor mem_fn wraps that pointer. The variable that stores the result can have any name. The source uses draw because it matches the member function name, but that choice is only a convention.
Tracing a Call Through the Function Object
Now follow one call from start to finish. The diagram shows the three pieces that meet at the call: the member function pointer that goes into mem_fn, the function object that comes out, and the object pointer p that is passed in as the argument. The member function runs on the object that p points to.
Calling draw through a function object
Given a Shape object s, a pointer p = &s, and auto draw = std::mem_fn(&Shape::draw);, what does the call draw(p) do?
- 1Create the function object
mem_fntakes the member function pointer &Shape::draw and returns a function object, which is stored in draw. - 2Identify the object argument
The call
draw(p)passes the object pointer p as the first argument. This is the ordinary argument that supplies the object. - 3Run the member function
The function object runs the draw member function on the object that p points to.
draw(p) runs the draw member function on s, the same result as p->draw().
Mistakes That Break a mem_fn Call
The source names a small set of errors that appear again and again with mem_fn(). Each one changes what the call means, so learn to recognize them on sight.
Common mistake Omitting the object argument
What it looks likedraw();Why it failsThe member function needs an object to act on, and this call supplies none.
Do this insteadPass an object pointer of the matching class, such as draw(p).
Common mistake Dropping the address-of operator
What it looks likeauto draw = std::mem_fn(Shape::draw);Why it failsWithout the address-of operator, the expression does not match the pattern
mem_fn(&Class::member_function), so the adaptor does not receive a member function pointer.Do this insteadWrite std::mem_fn(&Shape::draw).Common mistake Treating the function object as the member function itself
What it looks likeShape::draw(p);Why it failsThe member function pointer &Shape::draw names the function, and the function object created from it is what gets called. Calling the member name directly skips the pattern the source teaches.
Do this insteadCall the function object stored from mem_fn, such as draw(p).
Common mistake Passing an argument that does not match the pattern
What it looks likedraw(s); where s is a Shape object and p is the pointer
Why it failsThe source pattern passes an object pointer of the matching class. An argument of a different form or class does not match what the call expects.
Do this insteadPass an object pointer of the matching class, such as draw(p).
Common mistake Creating the function object but never calling it, so the member function never runs
Checking a Failing Call in Order
When a mem_fn() call goes wrong, the failure usually shows up at the call, even when the mistake is in the creation line. Check the three places in the pattern in order, and stop at the first one that does not match what you expected.
- 1Check the member function pointer on the right side of
mem_fn(). It should name a real member of the class, written as &Class::member_function. - 2Check that the function object is created from that pointer and stored in a variable.
- 3Check that the call passes an object pointer of the matching class as its argument.
Practice Predicting the Call
Assume a class Shape has a member function draw, and s is a Shape object with p = &s. Write the mem_fn expression that creates a function object named draw from Shape's draw member, and then write the call that runs draw on the object p points to.
Hints
- The adaptor is
mem_fn, and its argument is the member function pointer &Shape::draw. - The object pointer p is the ordinary argument to the function object.
Key Takeaways
- The 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), the adaptormem_fnwraps the member function pointer, the address-of operator marks it as a pointer, and the class name joins to the member name with the scope operator. - Calling the function object with an object pointer, such as
draw(p), performs the member function call on the object that pointer refers to. - The most common errors are 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 function pointer, then the stored function object, then the object pointer argument, in that order.