Creating Function Objects with bind()
Placeholders for bind() live in the subnamespace std::placeholders.
Why bind() Needs Named Markers
When you work with bind(), you will often need placeholders. A placeholder is not a value you compute or store. It is a named marker that bind() understands. Before you can use a placeholder, you need to know where it lives, because placeholder names are not available just by writing them on their own. This article answers that first question, then moves to a related tool: the function adaptor mem_fn(), which wraps a member function pointer in a function object that you can call.
The two topics share one idea. Both turn something that is not directly callable in the usual way into a callable function object. Placeholders describe positions that bind() treats specially, and mem_fn() turns a member function into something that can be invoked like a nonmember function. Keep that idea in mind as you read the sections below.
Where Placeholders Live
Placeholders for bind() live in the subnamespace std::placeholders. The outer namespace is std, the namespace of the C++ standard library. The inner namespace is placeholders, which sits inside std. The two parts are joined with the scope operator, so the full name is std::placeholders. The source calls it a subnamespace because it is nested within std rather than standing alone.
The placeholders are grouped in one dedicated namespace instead of being scattered across the standard library. This keeps them separate from ordinary functions and values. Understanding this grouping is the foundation for every later use of placeholders in bind(), because the name you write must lead to the namespace where the placeholder is actually declared.
Read the diagram from top to bottom. The header provides the placeholders namespace, the standard library namespace holds it, and the placeholders inside it are the items that bind() uses. The diagram shows structure only. The source does not describe how each placeholder maps onto the arguments of a bind() call; it says those argument-mapping details require the full reference, so this article does not diagram them.
What a Placeholder Stands For
The source states that placeholders are used in the bind() function. Their purpose is to serve as stand-ins that you supply to bind(). When you use bind(), a placeholder identifies a position that bind() treats specially, rather than a concrete value you pass in at that moment. This is why a placeholder must be reachable from the std::placeholders namespace: bind() needs to recognize it as a marker and not as an ordinary value.
Predicting a Member Function Call
A member function normally needs an object to act on. Think about a pointer p that points to a Shape object. The call p->draw() uses the pointer to find the object and then runs draw on it. The member function pointer &Shape::draw names the function without naming any particular object. The question for this section is what happens when that member function pointer is wrapped by mem_fn() and the resulting function object is called with p as an argument.
What do you think happens?
A function object draw was created with mem_fn(&Shape::draw), and p is a pointer to a Shape object. What does the call draw(p) do?
Reveal answer
It performs the member function call draw on the object that p points to.
The source states that calling the function object with an object pointer, such as draw(p), performs the member function call on that object. The object pointer is supplied as an ordinary argument, and the function object uses it to find the object to act on.
Reading mem_fn(&Class::member_function)
The syntax form mem_fn(&Class::member_function) is the pattern to remember. The name of the class appears before the member function name, joined with the scope operator. The address-of operator marks the whole class-qualified name as a pointer to the member function. Together these pieces form the argument that mem_fn() receives.
Read the expression from left to right. The outer name mem_fn is the adaptor: it takes the member function pointer and produces a function object. Inside the parentheses, the address-of operator and the class scope together identify a specific member function of a specific class. The member function name at the end is the function being wrapped. The source uses the variable name draw for the resulting function object, which matches the member function name, but the variable could have any name.
What mem_fn Produces
The function adaptor mem_fn(mf) produces a function object that can be called as a nonmember function. The argument mf is the member function pointer. The result is a value you store and call like any other function object, with the object pointer supplied as an ordinary argument.
This is useful because the member function, on its own, cannot be called without first naming an object. The function object hides that structure behind a plain call. You can pass the wrapper to code that expects something it can call, and supply the object as the argument at the moment of the call.
Member Pointers Versus Ordinary Function Pointers
The reason mem_fn() is needed comes from what a member function requires at the call site. The table below contrasts the two kinds of pointer using only what the source supports: a member function normally needs an object to act on, while the member function pointer names the function without naming any object.
| Aspect | Ordinary function | Member function |
|---|---|---|
| What it names | A function that stands alone | A function that belongs to a class |
| What the call needs | Its arguments only | An object to act on, plus its arguments |
| How it is wrapped by mem_fn() | Not applicable in this article | Wrapped into a function object by mem_fn(&Class::member_function) |
| Where the object comes from | Not required | An object pointer supplied at the call, such as draw(p) |
Why a member function pointer cannot be called on its own
The key contrast is the last row. A member function pointer alone is not enough to make the call, because the function needs an object. The function object produced by mem_fn() carries the member function, and the object is provided later as an argument.
Calling mem_fn From an Object Pointer
The example below does two things that look similar. It first calls the 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.
#include <functional>
#include <iostream>
class Shape {
public:
void draw() { std::cout << "drawing shape\n"; }
};
int main() {
Shape s;
Shape* p = &s;
p->draw();
auto draw = std::mem_fn(&Shape::draw);
draw(p);
}- 1The header <functional> provides
mem_fn. - 2Shape declares the member function draw, which writes a line of text.
- 3p points to the Shape object s, so p->
draw()finds the object through the pointer and runs draw on it. - 4The line auto draw = std::
mem_fn(&Shape::draw)creates the function object. This is the creation line. - 5The line
draw(p)is the call. The object pointer p is the argument, and the function object performs the member function call on the object p points to.
- p->
draw()calls the member function directly through the pointer - &Shape::draw is the member function pointer
mem_fnwraps the pointer into a function object named drawdraw(p)passes the object pointer as an ordinary argument
drawing shape
drawing shapedrawing shape
drawing shapeThe two lines produce the same visible effect, but they take different routes. The direct call uses the pointer in the usual member call form. The second call goes through the function object, which keeps the member function pointer and receives the object pointer as an argument at the moment of the call.
Follow the messages in order. The caller passes the object pointer p to the function object. The function object then performs the member function call on the object that p points to. The pointer is not itself the thing being drawn; it is the route to the object that the member function acts on.
Where mem_fn Calls Go Wrong
Most failures in this pattern come from three places: the member function pointer, the function object, and the call. The source lists the common errors as omitting the object argument, dropping the address-of operator, and treating the function object as the member function itself.
Common mistake Omitting the object argument
What it looks likedraw() written where draw(p) was intendedWhy it failsThe member function needs an object to act on, and the function object receives that object as its argument.
Do this insteadPass an object pointer of the matching class, such as draw(p).
Common mistake Dropping the address-of operator
What it looks likemem_fn(Shape::draw) instead of mem_fn(&Shape::draw)Why it failsThe source marks the whole class-qualified name as a pointer to the member function with the address-of operator, and that pointer is what
mem_fn()wraps.Do this insteadWrite mem_fn(&Class::member_function) with the address-of operator in front of the class-qualified name.
Common mistake Treating the function object as the member function itself
What it looks likeExpecting draw to be the Shape member function rather than a wrapper around it
Why it failsmem_fn()produces a separate function object. The member function is wrapped inside it.Do this insteadCall the function object with an object pointer, and do not expect it to behave like the bare member function.
Common mistake Pointing the wrong class at the member
What it looks likemem_fn(&Circle::draw) when draw belongs to ShapeWhy it failsThe member function pointer on the right side of
mem_fn()must name a real member of the class.Do this insteadCheck that the class name in the pointer is the class that declares the member function.
Common mistake Passing an object of the wrong class
What it looks likeCalling draw(q) where q points to an object of an unrelated class
Why it failsThe object pointer must match the class of the member function.
Do this insteadPass an object pointer of the matching class.
When a mem_fn() call goes wrong, the useful question is where the result first differs from what you expected. Check the three places in the pattern in order. First, the member function pointer should name a real member of the class. Second, the function object should be created from that pointer and stored in a variable. Third, the call should pass an object pointer of the matching class. A failure in any one of these steps appears at the call, even when the mistake lives in the creation line.
Checking a mem_fn Call Step by Step
A program creates auto draw = std::mem_fn(&Shape::draw); and has a pointer Shape* p that points to a Shape object. Which call correctly uses the function object, draw() or draw(p)? Explain which of the three checks from this article the wrong call fails.
Hints
- Recall that the function object needs an object pointer as its argument.
- Look at the three places to check: the member function pointer, the function object creation, and the call.
- The call that omits the object argument fails the third check.
Rewrite std::mem_fn(Shape::draw) so that it follows the pattern mem_fn(&Class::member_function). Then name the adaptor, the class scope, and the member function pointer in your rewritten expression.
Hints
- The address-of operator belongs in front of the class-qualified name.
- The adaptor is
mem_fn, the class scope is Shape::, and the member function name is draw.
Key Takeaways
- Placeholders for
bind()live in the subnamespace std::placeholders, which is nested within std and provided through the <functional> header. - A placeholder is a named marker that
bind()understands, not a value you compute or store, so it must be reachable from std::placeholders to be used. - The function adaptor
mem_fn(mf)takes a member function pointer and produces a function object that can be called as a nonmember function, with the object pointer supplied as an ordinary argument. - The pattern
mem_fn(&Class::member_function)combines the adaptor, the class scope, and the address-of operator to name a member function pointer. - Mistakes such as omitting the object argument, dropping the address-of operator, or treating the function object as the member function itself are the usual causes of failed calls.