Overview

3 Function objects

This chapter surveys everything C++ can treat as a “callable,” showing how to use higher-level, functional-style abstractions without sacrificing performance. It distinguishes ordinary functions from function objects (anything invocable with f(args...)), covering function pointers/references, classes that overload operator(), and template-based, generic function objects. Practical guidance includes return type options (trailing types, automatic deduction, decltype(auto)), plus correct argument/result forwarding with forwarding references and std::forward to preserve value categories and avoid unnecessary copies.

A large part of the chapter centers on lambdas as concise, inline function objects. It explains capture lists (by value, by reference, mixed and default captures), the fact that lambda call operators are const by default and when to use mutable, and what compilers generate under the hood (a closure class with captured state and an operator()). It also introduces generalized lambda captures for creating or moving state (e.g., move-only types like unique_ptr) directly into the closure, and generic lambdas whose parameters use auto so a single lambda can adapt to many argument types, enabling succinct, local definitions for algorithms and higher-order utilities.

For terser and reusable predicates, the chapter demonstrates hand-rolled function objects (e.g., an error-testing predicate with convenient operators), the standard library’s operator wrappers (plus, multiplies, greater, etc.), and expression-style composition with placeholder-based libraries such as Boost.Phoenix—along with the trade-off of slower compile times. Finally, it covers std::function as a type-erased wrapper that can hold any callable matching a given signature (including lambdas, functors, function pointers, and even member pointers), noting its convenience across interfaces and separate compilation units but also its runtime costs (type erasure, potential dynamic allocation) and constness caveats. The overarching advice is to favor concrete functors, lambdas, and templates for optimal performance, reserving std::function for when explicit, uniform callable types are required.

You can use an ordinary function that checks whether a person is more than 42 years old when you want to count the number of people over that age limit. But with this approach, you have the ability to check the age against a single predefined value.
c03_01.png
When creating the predicate, it’s useful not only to allow the specification of different age limits, but also to support different types that have the age information.
c03_02.png
Getting all females from a group of people. You’ve been using a predicate called is_female, which is a non-member function for this. Now you’re going to do the same with a lambda.
c03_03.png
c03_g01.eps
You’re passing a lambda as a predicate to the std::count_if algorithm. The lambda receives a single employee and checks whether a person belongs to the team you’re checking the count for. The std::count_if algorithm will count all the yeses and return the answer.
c03_04.png
You’re filtering the responses based on whether they contain an error, in order to process them separately.
c03_05.png
The Phoenix expression is composed of captured values and placeholders that represent the function arguments. When the arguments are provided by calling the function object, the expression is evaluated and the calculated value returned as the result of the function.
c03_06.png

Summary

  • It’s possible to use objects that are castable to function pointers as if they were ordinary functions, but the call operator is the preferred way of creating function objects.
  • For functions whose return type isn’t important enough to be explicitly stated, you can let the compiler deduce it from the value you’re returning by using the auto keyword.
  • Using the automatic return type deduction avoids any type of conversion or narrowing that might arise when you specify the type explicitly (for example, returning a double value in a function that should return an int).
  • If you want to create a function object that can work on a multitude of types, you should make its call operator a function template.
  • Lambdas are useful syntactic sugar for creating function objects. Using them is usually better than writing whole classes with call operators by hand.
  • Lambdas in C++14 have become a true replacement for most function objects that you could write by hand. They add new possibilities when capturing variables, and they support creating generic function objects.
  • Although the lambdas provide terser syntax for creating function objects, they’re still overly verbose for some cases. In those situations, you can use the libraries such as Boost.Phoenix, or roll out your own function objects.
  • The std::function is a useful, but it comes with a performance penalty equivalent to a virtual function call.

FAQ

What is a function object in C++, and how does it differ from an ordinary function?A function object is anything that can be called with the function-call syntax f(args...). This includes: - Ordinary functions and function pointers/references - Objects that overload operator() - Lambdas (anonymous function objects) Unlike ordinary functions, function objects can hold state (data members), enabling customized behavior per instance.
How does automatic return type deduction work, and what are common pitfalls?Since C++14, you can omit the return type and let the compiler deduce it from return statements. All return statements must deduce to the same type, otherwise it’s an error. For recursive functions with auto, ensure a non-recursive return appears before a self-call, or specify the return type explicitly.
When should I use decltype(auto) instead of auto for a function’s return type?Use decltype(auto) when you want to perfectly forward the return value, preserving value category and references. For example, returning (x) with decltype(auto) yields a reference; returning x yields a value. This is useful in wrapper/forwarding functions where you don’t want to force a copy or a reference incorrectly.
What are forwarding references, and how do I forward arguments correctly?A forwarding reference is a T&& parameter in a deduced context (template). It can bind to lvalues, rvalues, const, and non-const. Forward the argument with std::forward(arg) to preserve its value category and constness, avoiding unnecessary copies or blocking valid bindings.
How do function pointers and objects convertible to function pointers fit into function objects?Function pointers and references are callable and thus function objects. Types that can implicitly convert to a function pointer are also callable, but prefer proper function objects/lambdas for clarity and power. Conversions are mainly useful for interop with C APIs that require function pointers.
How do I create a stateful function object, and how can I make it generic?Create a class that stores the state and overloads operator(). Example: store an age limit and test objects’ age(). To make it generic: - Either template the class on the object type, or - Template the operator() so the same instance works with any type that provides the required API (duck-typing).
How do lambda captures work, and what are generalized captures and mutable lambdas?Captures control which outer-scope variables are stored in the closure: - [a, &b]: capture a by value, b by reference - []: capture nothing - [&] or [=]: capture all used variables by reference or by value - [this]: capture this - [&, a] or [=, &b]: mix capture modes Generalized (init) captures let you create/initialize members, e.g. [p = std::move(ptr), t = now()]. Lambdas are const by default; mark mutable to modify captured-by-value state inside the body. Be careful with reference captures’ lifetimes.
What are generic lambdas, and how do they deduce types?Generic lambdas declare parameters as auto; their operator() is a function template. Types are deduced at the call site, so the same lambda can work with different argument types. From C++20, you can also write template parameters explicitly: [](T a, T b).
Can I write function objects terser than lambdas?Sometimes. Options include: - Operator wrappers in the standard library (std::plus, std::greater, std::logical_and, etc.) for common operations - Writing small reusable operator()-based objects (e.g., an error predicate with operator! and operator== for readable filters) - Libraries like Boost.Phoenix that provide placeholders (arg1, arg2) and operator composition. These can be very terse but may slow compilation; prefer lambdas for complex logic.
What is std::function, what can it wrap, and when should I use it?std::function type-erases any callable with the given signature: free functions, function pointers, lambdas (generic ones if convertible), function objects, even member function pointers (called via std::function). Use it when: - You need a concrete, non-template type (e.g., class member, interface boundary) - You store/transport callables across translation units Costs: type erasure and possible heap allocation; limited inlining; const-call operator can still invoke non-const callables. Prefer templates/auto for zero-overhead when possible. Small-function-object optimization may avoid allocations for small callables.

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