Concepts / Creating Function Objects with bind()

Creating Function Objects with bind()

Placeholders for bind() live in the subnamespace std::placeholders.

  • Programming

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

Definition

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?

  • It draws the pointer p itself as a shape

  • It performs the member function call draw on the object that p points to

  • It creates a new Shape object each time it is called

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

Definition

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.

AspectOrdinary functionMember function
What it namesA function that stands aloneA function that belongs to a class
What the call needsIts arguments onlyAn object to act on, plus its arguments
How it is wrapped by mem_fn()Not applicable in this articleWrapped into a function object by mem_fn(&Class::member_function)
Where the object comes fromNot requiredAn 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.

Code walkthrough
#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);
}
  1. 1
    The header <functional> provides mem_fn.
  2. 2
    Shape declares the member function draw, which writes a line of text.
  3. 3
    p points to the Shape object s, so p->draw() finds the object through the pointer and runs draw on it.
  4. 4
    The line auto draw = std::mem_fn(&Shape::draw) creates the function object. This is the creation line.
  5. 5
    The 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_fn wraps the pointer into a function object named draw
  • draw(p) passes the object pointer as an ordinary argument
Output
drawing shape
drawing shape
Output
drawing shape
drawing shape

The 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 like
    draw() written where draw(p) was intended
    Why it fails

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

    Do this instead

    Pass an object pointer of the matching class, such as draw(p).

  • 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 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 instead

    Write 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 like

    Expecting draw to be the Shape member function rather than a wrapper around it

    Why it fails

    mem_fn() produces a separate function object. The member function is wrapped inside it.

    Do this instead

    Call 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 like
    mem_fn(&Circle::draw) when draw belongs to Shape
    Why it fails

    The member function pointer on the right side of mem_fn() must name a real member of the class.

    Do this instead

    Check 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 like

    Calling draw(q) where q points to an object of an unrelated class

    Why it fails

    The object pointer must match the class of the member function.

    Do this instead

    Pass 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

EASY

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.
MEDIUM

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.

Related concepts

More concepts starting with C