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.
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.
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.
What mem_fn() Does to a Member Function
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.
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.
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.
empties equals 2, because two of the four strings are empty- words holds four strings, two of which are empty
- &std::string::empty is the address of the member function
mem_fnwraps that member function into a callable objectcount_ifcalls 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?
Reveal 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.
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.
lengths holds 3, 3, 5- std::
back_inserterappends each result to lengths - std::
mem_fn(&std::string::size)is the only adapted piece - transform calls the wrapper once per word
Tracing transform with size()
Collect the length of each word in a vector using mem_fn() and transform.
- 1Take the first element
transform hands the string cat to the
mem_fnwrapper. - 2Call the member function
The wrapper performs the member call
size()on cat, which gives 3. - 3Store the result
back_inserterappends 3 to lengths. - 4Repeat for each element
The same steps give 3 for dog and 5 for horse.
lengths contains 3, 3, 5
Mistakes Beginners Make with mem_fn()
Common mistake Passing the member function without taking its address
What it looks likestd::mem_fn(std::string::empty) instead of std::mem_fn(&std::string::empty)Why it failsThe member function must be referred to by its address so
mem_fncan receive it as a member function to wrap.Do this insteadWrite the ampersand before the class name, as in &std::string::empty.
Common mistake Using mem_fn() on an ordinary nonmember function
What it looks likeWrapping a free function such as a standalone is_short(string) with mem_fn
Why it failsmem_fnis meant for member functions only, and the source limits its use to them.Do this insteadPass 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 likeAssuming that wrapping empty() with mem_fn makes it count characters instead
Why it failsmem_fnchanges only how the function is reached. The behavior of the member function is unchanged.Do this insteadRemember 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 likeWriting std::count_if(words.begin(), words.end(), words[0].empty()) and expecting per-element checksWhy it failsThe 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 insteadPass the wrapped member function, std::mem_fn(&std::string::empty), so the algorithm can invoke it on every element.
Practice Wrapping the Right Function
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_ifcall 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_ifwithempty()counts matching elements, and transform withsize()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.