Overview

4 Creating new functions from the old ones

This chapter explores practical techniques for generating new functions from existing ones to improve reuse, clarity, and adaptability in C++. It shows how thinking in terms of small, composable transformations enables you to fit functions to algorithm requirements, build pipelines of operations, and scale simple ideas to broader contexts. The core themes are partial function application, currying (viewing multiargument functions as chains of unary functions), function composition, and lifting functions to operate over richer structures.

The discussion begins with partial function application: fixing some arguments of a function to create a new, more specialized callable. It progresses from hand-rolled functors to a generic “bind second” wrapper, then to std::bind and its placeholders for selective binding and argument reordering, including binding member functions (with std::ref for references) and adapting callables for standard algorithms. The chapter contrasts std::bind’s terse flexibility with lambdas’ readability and optimizer-friendliness, showing equivalent lambda formulations for the same adaptations. Concrete examples include creating predicates like “greater than 42,” converting degrees to radians by partially applying multiplication, sorting via reversed comparator arguments, and tailoring printing functions for people by pre-binding stream and format.

Currying reframes any multiargument function as a sequence of unary functions, enabling you to supply arguments incrementally and even write generic helpers such as make_curried. A database case study demonstrates how curried APIs reduce overload explosions and make it trivial to specialize on the first argument without knowing the remaining arity—something that becomes cumbersome with std::bind. The chapter then applies function composition to build a clear pipeline for the “most frequent words” problem by chaining small transformations. Finally, it revisits function lifting: starting from a function on a single value (like uppercasing a string), you systematically “lift” it to pointers, containers, and maps, and generalize patterns such as reversing pairs across collections—illustrating how functional and object-oriented styles reinforce each other through reusable, composable building blocks.

On a higher level, you have a function of two arguments: one object, and an integer value representing the age limit. The function returns whether the object is older than that age limit. When you fix the age limit to a specific value, you get a unary function that compares the object’s age against that predefined value.
c04_01.png
Fixing the second argument of the greater-than comparison operator to 42 turns it into a unary predicate that tests whether a value is greater than 42.
c04_02.png
Binding one argument of multiplication to π/180 gives you a function that converts degrees to radians.
c04_03.png
The two-argument function tests whether its first argument is greater than its second. Binding both arguments to specific values such as 6 and 42 creates a nullary function object that, when called, returns the result of greater(6, 42).
c04_04.png
The two-argument function tests whether its first argument is greater than its second. Binding one of the arguments to a value and the other to a placeholder creates a unary function object that, when called with a single argument, uses that argument to fill the hole defined by the placeholder.
c04_05.png
You specify the first placeholder to be the second argument passed to greater, and vice versa. This results in a new function that’s the same as greater, but with the arguments switched.
c04_06.png
You want to create a function that prints names of people to the standard output. You have a function that can write a person’s information in multiple formats and to different output streams. You specify the output stream and the format while leaving the person empty, to be defined by the std::for_each algorithm.
c04_07.png
If you want to use lambdas instead of std::bind, you need to convert bound arguments to captured values; the unbound arguments will become lambda arguments.
c04_08.png
The function takes two arguments, and you’re converting it to a unary function that returns not a value, but a new unary function. This allows you to pass in the arguments one at a time rather than all at once.
c04_09.png
You started with a function of three arguments and converted it to its curried version. The curried version is a function that accepts a person and returns a function that takes an output stream. That function, again, returns a new function that takes the output format and prints the person’s info.
c04_10.png
Different applications have different needs; some may want to connect to multiple databases, some may need multiple sessions, and some need just a single session while connected to a single database.
c04_11.png
You’re transforming the original text to a list of words, assigning a count to each word, and then sorting the words based on the number of times they appear in the text.
c04_12.png
You’re lifting a function that transforms a single string to a few functions that transform strings contained in various container-like types.
c04_13.png
You have a collection of pairs that you want to reverse and a function that’s capable of reversing a single pair. By lifting the function, you get a function that can reverse each pair in a collection.
c04_14.png

Summary

  • Higher-order functions such as std::bind can be used to transform existing functions into new ones. You can get a function of n arguments and easily turn it into a unary or a binary function that you can pass to algorithms such as std::partition or std::sort with std::bind.
  • Placeholders give you a high level of expressiveness when defining which function arguments shouldn’t be bound. They allow you to reorder the arguments in the original function, to pass the same argument multiple times, and so forth.
  • Although std::bind provides a terse syntax to do partial function application, it might have performance penalties. You may want to consider using lambdas when writing performance-critical code that’s often invoked. They’re more verbose, but the compiler will be able to optimize them better than the function objects created by std::bind.
  • Designing an API for your library is hard. Many use cases need to be covered. Consider creating an API that uses curried functions.
  • One of the often-heard statements about functional programming is that it lets you write shorter code. If you create functions that are easily composed, you can solve complex problems with a fraction of the code the usual imperative programming approach would take.

FAQ

What does partial function application mean in C++?It’s the technique of creating a new function by fixing one or more arguments of an existing function to specific values, while leaving the rest to be supplied later. For example, fixing the second argument of the binary operator> to 42 yields a unary predicate that checks if a value is greater than 42.
How can I turn a binary function into a unary predicate (e.g., “greater than 42”)?You can: - Write a small function object that stores the fixed value and compares against it. - Use a generic wrapper (bind2nd-like) that captures a binary callable and one argument. - Use std::bind: std::bind(std::greater(), std::placeholders::_1, 42). - Use a lambda: [](int x){ return x > 42; }.
What is std::bind, and how do placeholders like _1 and _2 work?std::bind creates a function object by binding some or all arguments of a callable. Bound values are stored; unbound positions are marked with placeholders from std::placeholders (e.g., _1, _2). When you call the bound object, the call-time arguments fill the placeholders in the order indicated. Example: auto f = std::bind(std::greater(), _1, 42); then f(6) compares greater(6, 42).
Can std::bind reorder or reverse arguments? How about member functions?Yes. You can reorder by arranging placeholders: std::bind(std::greater(), _2, _1) reverses the two arguments. For member functions, bind the pointer-to-member and supply the instance (or placeholder) first: std::bind(&person_t::print, _1, std::ref(std::cout), person_t::name_only) creates a unary function that calls person.print(...).
How do I bind references (e.g., std::cout) with std::bind?By default, std::bind stores copies. To bind by reference use std::ref or std::cref: std::bind(print_person, _1, std::ref(std::cout), person_t::name_only). This is required for noncopyable or heavyweight objects.
When should I prefer lambdas over std::bind for partial application?- Lambdas are core-language features and typically optimize better. - They capture exactly what you ask (by value/reference), improving clarity and efficiency. - They are often more readable, though slightly more verbose. - Use std::bind if you really need terse placeholder-based reordering; otherwise prefer a lambda.
What is currying, and how is it different from partial function application?Currying converts an n-argument function into a chain of unary functions. Each call supplies the next argument and returns a new unary function until all arguments are provided. Partial application fixes some arguments of an existing function to produce a new function. Key differences: - Currying binds arguments in order (first to last); partial application can bind any subset in any order. - Curried functions are uniformly unary, which is powerful in generic contexts where arity is unknown.
How can currying simplify real APIs (e.g., database query/update) and generic adapters?With a curried API like query(conn)(session)(table)(filter): - You can easily specialize just the first argument (e.g., fix a connection) and reuse the resulting function. - The same pattern works for other operations (update, delete) regardless of their remaining arity. - You can write generic helpers like for_local_connection(f) { return f(local_connection); } that work for any curried function without counting placeholders.
What is function composition, and how do I structure code to compose transformations?Function composition chains small, focused transformations so the output of one feeds the next. Example pipeline for “most common words”: - words(text) → vector<string> - count_occurrences(words) → unordered_map<string, uint> - reverse_pairs(map) → vector<pair<uint, string>> - sort_by_frequency(pairs) → sorted vector - print_pairs(sorted) Compose them at the call site: print_pairs(sort_by_frequency(reverse_pairs(count_occurrences(words(text))))).
What is function lifting, and how do I lift a function to work on pointers or collections?Lifting adapts a function that works on a value to operate over a broader context containing that value. - Pointer lift: given f: T& → void, create g: T* → void that applies f if the pointer is non-null. - Collection lift: given f: T& → void, create g: Iterable<T>& → void that applies f to each element. You can also lift transformations like swapping elements of a pair to act on entire collections of pairs (e.g., reverse_pairs over vectors/maps).

pro $24.99 per month

  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose one free eBook per month to keep
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime

lite $19.99 per month

  • access to all Manning books, including MEAPs!

team

5, 10 or 20 seats+ for your team - learn more


choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Functional Programming in C++ ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Functional Programming in C++ ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Functional Programming in C++ ebook for free