Concepts / Using mem_fn() for Non-Member Function Calls

Using mem_fn() for Non-Member Function Calls

mem_fn() changes how the function is reached, not what it does. The empty() member still checks emptiness, and size() still reports length. Only the calling style is adapted.

  • Programming

Why Algorithms Cannot Use the Dot Syntax

Suppose you have a collection of strings and you want to count how many of them are empty. You already know how to ask one string whether it is empty: you call its member function on that string. The question this article answers is what to do when the work is not done by you directly, but by an algorithm that walks through the collection on your behalf.

The algorithm has to perform the operation for every element, and the way it performs that operation is set by the rules of the algorithm, not by your own code. So the real issue is not whether the operation exists, but the shape of the call the algorithm is able to make on each element.

EASY

Before reading on, pause and think: if an algorithm is handed a list of strings and told to check each one, what kind of call would it need to make on each element? Keep your answer in mind, because the rest of this article is built around it.

Hints
  • Think about how you normally write a call on one specific string.
  • Think about what the algorithm has in hand when it visits each element.

Two Ways to Call the Same Operation

A member function is attached to an object. You write the object first and then the operation, as in the expression object.operation(). An algorithm does not usually know your object by name. Instead, it takes an element from the sequence and applies an operation to that element. That is closer to writing operation(element), with the element handed in as an argument.

The source identifies the major use of mem_fn() as the case where an algorithm requires an operation to be called as a nonmember function. In other words, the algorithm is written to call its operation in the nonmember style, with the object supplied as an ordinary argument. A member function, written in the object-first style, does not fit that calling pattern on its own. This gap is what mem_fn() is meant to close.

words[0]object written firstemptyoperation called on its own.empty()operation attached toobjectwords[0] as argumentobject passed indot callreceives argument
Where does the object go when a member call is reshaped into a nonmember call, and how do the two call forms differ?

What mem_fn() Does to a Member Function

Definition

mem_fn() takes a member function and returns a callable object that the algorithm can invoke, with each element acting as the object. The algorithm supplies the element, and the callable object performs the member call on it.

In the real world

Think of mem_fn() as a translator placed between the algorithm and the member function. The algorithm speaks in the nonmember style, passing each element as an argument. The translator receives that element and performs the member call on it. Nothing about the member function itself changes; only the way it is reached is reshaped so that the algorithm can use it.

Key point

mem_fn() changes how the function is reached, not what it does. The empty() member still checks emptiness, and size() still reports length. Only the calling style is adapted.

Tracing count_if Over a Vector of Strings

The standard library function count_if counts the elements of a range that satisfy a condition. The member function empty() from the string class is the condition. Wrapping it with mem_fn() lets count_if apply it to each string in the vector. The following example is an original illustration built for this article.

cpp
Output
empties equals 2, because two of the four strings are empty
Notes on the code
  • words holds four strings, two of which are empty
  • &std::string::empty is the address of the member function
  • mem_fn wraps that member function into a callable object
  • count_if calls the wrapper once per element

What do you think happens?

Given the vector above with two empty strings and two non-empty strings, what value does count_if return?

  • 0

  • 2

  • 4

Reveal answer
Answer:

2

count_if applies the wrapped empty() to each element and counts the elements for which it returns true. Exactly two strings are empty, so the count is 2.

appleempty is false, not countedempty stringempty is true, countedpearempty is false, not countedempty stringempty is true, countedcount is 2final resultnext elementnext elementnext elementend of range
How does the algorithm step through each string and call the wrapped member function on it, one element at a time?
count_ifalgorithmmem_fn wrappercallable objectstring elementobjectpasses elementcalls empty()returns true or falsereturns result
What does the algorithm call, and how does that call pass through the mem_fn wrapper to reach the string's member function?

Transforming Strings into Their Lengths

The same adaptor works with algorithms that produce new values rather than counts. Here the member function size() reports the length of each string, and the transforming algorithm collects those lengths into a second container. The only change from the previous example is the member function being adapted and the algorithm being used. The wrapping step is identical.

cpp
Output
lengths holds 3, 3, 5
Notes on the code
  • std::back_inserter appends each result to lengths
  • std::mem_fn(&std::string::size) is the only adapted piece
  • transform calls the wrapper once per word
Worked example

Tracing transform with size()

Collect the length of each word in a vector using mem_fn() and transform.

  1. 1
    Take the first element

    transform hands the string cat to the mem_fn wrapper.

  2. 2
    Call the member function

    The wrapper performs the member call size() on cat, which gives 3.

  3. 3
    Store the result

    back_inserter appends 3 to lengths.

  4. 4
    Repeat for each element

    The same steps give 3 for dog and 5 for horse.

Result

lengths contains 3, 3, 5

Mistakes Beginners Make with mem_fn()

  • Common mistake

    Passing the member function without taking its address

    What it looks like
    std::mem_fn(std::string::empty) instead of std::mem_fn(&std::string::empty)
    Why it fails

    The member function must be referred to by its address so mem_fn can receive it as a member function to wrap.

    Do this instead

    Write the ampersand before the class name, as in &std::string::empty.

  • Common mistake

    Using mem_fn() on an ordinary nonmember function

    What it looks like

    Wrapping a free function such as a standalone is_short(string) with mem_fn

    Why it fails

    mem_fn is meant for member functions only, and the source limits its use to them.

    Do this instead

    Pass an ordinary function directly to the algorithm, or wrap only true member functions with mem_fn.

  • Common mistake

    Expecting mem_fn to change what the function does

    What it looks like

    Assuming that wrapping empty() with mem_fn makes it count characters instead

    Why it fails

    mem_fn changes only how the function is reached. The behavior of the member function is unchanged.

    Do this instead

    Remember that empty() still checks emptiness and size() still reports length; only the calling style differs.

  • Common mistake

    Calling the member function inside the algorithm call

    What it looks like
    Writing std::count_if(words.begin(), words.end(), words[0].empty()) and expecting per-element checks
    Why it fails

    The call words[0].empty() runs once on a single object, so the algorithm never receives a callable that it can apply to each element.

    Do this instead

    Pass the wrapped member function, std::mem_fn(&std::string::empty), so the algorithm can invoke it on every element.

Practice Wrapping the Right Function

MEDIUM

A vector holds the strings red, blue, and green. Write the algorithm call that would count how many strings have a length of zero using the empty() member function wrapped with mem_fn(). Then explain in one sentence what the wrapper is doing with each element.

Hints
  • The member function is empty, so refer to it as &std::string::empty.
  • The count_if call needs the iterator range and the wrapped function as its last argument.

Key Takeaways

  • Some algorithms call their operation in the nonmember style, with the object passed as an argument, while member functions are written in the object-first style.
  • mem_fn() takes a member function and returns a callable object that the algorithm can invoke, with each element acting as the object.
  • The pattern is the same across algorithms: count_if with empty() counts matching elements, and transform with size() collects lengths.
  • mem_fn() changes how a member function is reached, not what it does, and it should be used with member functions only, passed by their addresses.

Related concepts

More concepts starting with U