Concepts / Member Functions and the this Pointer

Member Functions and the this Pointer

The function adaptor mem_fn(mf) produces a function object that can be called as a nonmember function.

  • Programming

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.

cpp
Notes on the code
  • 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?

  • Yes, both run draw on the object that p points to

  • No, draw(p) runs a different, nonmember function

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

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

&Shape::drawnames the member onlymem_fn(&Shape::draw)result of the adaptorp->draw()call uses the pointerbefore the arrowdraw(p)called like a nonmemberfunctionwrapped by mem_fncalled with object pointer
How does a raw member function pointer differ from the callable function object that mem_fn wraps around it?

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.

mem_fnfunction adaptor&address-of marks a memberfunction pointerShapeclass that owns the member::scope operator joins classand memberdrawmember function name
Which piece is the adaptor, which is the member function pointer, and which names the class?

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.

&Shape::drawmember function pointerfunction objectcreated by mem_fn andstored as drawdraw member functionruns on the object p pointstopShape object pointer, theargumentpassed to mem_fncalled as draw(p)supplies the object
When I write mem_fn(&Shape::draw)(p), where does p go, and how does it become the object the member function acts on?
Worked example

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?

  1. 1
    Create the function object

    mem_fn takes the member function pointer &Shape::draw and returns a function object, which is stored in draw.

  2. 2
    Identify 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.

  3. 3
    Run the member function

    The function object runs the draw member function on the object that p points to.

Result

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 like
    draw();
    Why it fails

    The member function needs an object to act on, and this call supplies none.

    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
    auto draw = std::mem_fn(Shape::draw);
    Why it fails

    Without 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 instead
    Write std::mem_fn(&Shape::draw).
  • Common mistake

    Treating the function object as the member function itself

    What it looks like
    Shape::draw(p);
    Why it fails

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

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

    draw(s); where s is a Shape object and p is the pointer

    Why it fails

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

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

  1. 1
    Check 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.
  2. 2
    Check that the function object is created from that pointer and stored in a variable.
  3. 3
    Check that the call passes an object pointer of the matching class as its argument.

Practice Predicting the Call

EASY

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 adaptor mem_fn wraps 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.

Related concepts

More concepts starting with M