SimplifyC++ Article
Everything You Need to Know About Functional header in C++
Everything You Need to Know About Functional header in C++
The <functional> header is one of the most powerful and versatile parts of the C++ Standard Library. It provides tools for working with callable objects – things you can invoke like functions. Whether you're using lambdas, function pointers, functors, or member functions, <functional> helps you store, bind, adapt, and pass them around seamlessly.
In this article, we’ll explore every major component of <functional> with clear, practical examples. By the end, you’ll know how to use std::function, std::bind, placeholders, std::reference_wrapper, std::mem_fn, and more.
1. What Is <functional>?
At its core, <functional> provides:
Polymorphic function wrappers (
std::function) – a type‑erased container for any callable.Function binders (
std::bind, placeholders) – to fix arguments or reorder them.Reference wrappers (
std::reference_wrapper,std::ref,std::cref) – to store references in copy‑heavy contexts.Member function adapters (
std::mem_fn) – to turn member functions into ordinary function objects.Function objects for arithmetic, comparisons, and logical operations (e.g.,
std::plus,std::greater,std::logical_and).
All of these are designed to work seamlessly with the <algorithm> library and modern C++ idioms.
2. std::function – The Universal Callable Wrapper
std::function is a type‑erased wrapper that can hold any callable object with a given signature. It’s like a super‑charged function pointer.
Basic Usage
cpp
int add(int a, int b) { return a + b; }
struct Multiply { int operator()(int a, int b) const { return a * b; }};
int main() { // Store a free function std::function<int(int,int)> func = add; std::cout << func(3, 4) << '\n'; // 7
// Store a lambda func = [](int a, int b) { return a - b; }; std::cout << func(10, 3) << '\n'; // 7
// Store a functor (function object) Multiply mult; func = mult; std::cout << func(5, 6) << '\n'; // 30
return 0;}
Why Use std::function?
Type erasure – you can store different callables in the same container (e.g.,
std::vector<std::function<bool(int,int)>>).Nullability – a
std::functioncan be empty; test it with!funcorfunc == nullptr.Target access – use
func.target<T>()to retrieve the stored object (advanced).
Performance Note
std::function has some overhead (virtual dispatch, possible small‑object optimisation). For hot paths, consider using templates or raw function pointers. But for callbacks, event systems, or configuration, it’s invaluable.
Example: Callbacks
cpp
class Button { std::vector<std::function<void()>> clickHandlers;public: void onClick(std::function<void()> handler) { clickHandlers.push_back(handler); } void click() { for (auto& h : clickHandlers) h(); }};
void playSound() { /* ... */ }
int main() { Button btn; btn.onClick(playSound); btn.onClick([](){ std::cout << "Button clicked!\n"; }); btn.click(); // calls both}
3. std::bind – Fixing and Reordering Arguments
Before lambdas (C++11), std::bind was the main way to create adapters. Today, lambdas are often clearer, but std::bind still shines in generic code or when you need to bind member functions.
Syntax
cpp
auto newCallable = std::bind(callable, arg1, arg2, ..., argN);
Placeholders _1, _2, … (from std::placeholders) represent arguments that will be supplied when the bound object is called.
Basic Examples
cpp
void print(int a, int b, int c) { std::cout << a << ", " << b << ", " << c << '\n';}
int main() { using namespace std::placeholders;
// Fix the first argument to 10 auto f1 = std::bind(print, 10, _1, _2); f1(20, 30); // prints: 10, 20, 30
// Reorder arguments: third becomes first, etc. auto f2 = std::bind(print, _3, _1, _2); f2(1, 2, 3); // prints: 3, 1, 2
// Bind a member function (requires an object pointer) struct Demo { void say(int x) { std::cout << x << '\n'; } }; Demo d; auto f3 = std::bind(&Demo::say, &d, _1); f3(42); // prints: 42}
When to Prefer std::bind Over Lambda?
Very generic code –
std::bindworks with any callable, including member pointers, without needingstd::mem_fn.Perfect forwarding of placeholders – lambdas require
decltypetricks.Compile‑time argument reordering – sometimes more concise than a lambda.
But lambdas are generally more readable. For new code, use lambdas unless you have a specific reason.
4. Placeholders – _1, _2, _3, …
Placeholders live in the std::placeholders namespace. They tell std::bind which arguments to forward and where.
cpp
void show(int a, int b) { std::cout << a << ' ' << b << '\n'; }
int main() { using namespace std::placeholders;
auto f = std::bind(show, _2, _1); // swap arguments f(100, 200); // prints: 200 100
// You can also ignore arguments: use _1 for unused position auto g = std::bind(show, 42, _2); // ignores first argument g(999, 123); // prints: 42 123}
5. std::ref and std::cref – Reference Wrappers
By default, std::bind and std::function copy their arguments. To store or pass a reference, use std::ref (non‑const) or std::cref (const). They create a std::reference_wrapper<T>.
Why Are They Needed?
cpp
void increment(int& x) { ++x; }
int main() { int a = 5; auto bad = std::bind(increment, a); // copies 'a'! bad(); std::cout << a << '\n'; // still 5 – not incremented
auto good = std::bind(increment, std::ref(a)); good(); std::cout << a << '\n'; // 6 – works!}
Using std::ref with std::function
cpp
std::function<void()> f = std::bind(increment, std::ref(a));
You can also store a reference wrapper directly:
cpp
std::reference_wrapper<int> refA = std::ref(a);refA.get() = 10; // modifies a
When to Use std::cref
When you want to pass a const reference, e.g., to avoid copying a large object into a lambda or bound callable.
cpp
void process(const std::string& s);std::string huge = "...";auto bound = std::bind(process, std::cref(huge)); // no copy
6. std::mem_fn – Wrapping Member Functions
std::mem_fn turns a pointer to a member function into a callable object that can be invoked with an object (or pointer) as the first argument.
Syntax
cpp
auto wrapper = std::mem_fn(&Class::method);wrapper(object, args...);wrapper(&object, args...);
Example
cpp
struct Person { std::string name; void greet() const { std::cout << "Hello, I'm " << name << '\n'; } void setAge(int a) { age = a; } int age = 0;};
int main() { std::vector<Person> people = {{"Alice"}, {"Bob"}};
// Call const member on each object auto greetFn = std::mem_fn(&Person::greet); for (Person& p : people) greetFn(p); // Alice, then Bob
// Use with std::for_each std::for_each(people.begin(), people.end(), greetFn);
// Non‑const member with pointer auto setAgeFn = std::mem_fn(&Person::setAge); Person alice{"Alice"}; setAgeFn(&alice, 30); // passes pointer setAgeFn(alice, 31); // also works with reference}
Comparison with std::bind & Lambdas
std::mem_fnis cleaner thanstd::bind(&Person::greet, _1).Lambdas are equally clear:
[&](Person& p){ p.greet(); }.Use
std::mem_fnwhen you already have a callable object and want to pass it to an algorithm without extra braces.
7. Built‑in Function Objects – Arithmetic, Comparisons, Logic
The <functional> header provides templated functors for common operations. These are useful with std::transform, std::sort, etc., and often allow the compiler to inline better than a hand‑rolled lambda.
Arithmetic
| Functor | Operation |
|---|---|
std::plus<T> | + |
std::minus<T> | - |
std::multiplies<T> | * |
std::divides<T> | / |
std::modulus<T> | % |
std::negate<T> | - (unary) |
Comparisons
| Functor | Operation |
|---|---|
std::equal_to<T> | == |
std::not_equal_to<T> | != |
std::greater<T> | > |
std::less<T> | < |
std::greater_equal<T> | >= |
std::less_equal<T> | <= |
Logical
| Functor | Operation |
|---|---|
std::logical_and<T> | && |
std::logical_or<T> | || |
std::logical_not<T> | ! |
Example
cpp
int main() { std::vector<int> v = {3, 1, 4, 1, 5}; // Sort in descending order std::sort(v.begin(), v.end(), std::greater<int>()); // v = {5,4,3,1,1}
// Transform: multiply each element by 2 std::vector<int> result; std::transform(v.begin(), v.end(), std::back_inserter(result), std::bind(std::multiplies<int>(), std::placeholders::_1, 2)); // result = {10,8,6,2,2}
// Using plus with accumulate int sum = std::accumulate(v.begin(), v.end(), 0, std::plus<int>()); std::cout << sum << '\n'; // 14}
8. Advanced: std::function with Move‑Only Types
std::function requires the stored callable to be copy‑constructible. For move‑only callables (e.g., a lambda capturing a std::unique_ptr), you need a different approach – like std::move_only_function (C++23) or a custom wrapper. In C++14/17, you can use std::packaged_task or design your own type‑erased interface.
cpp
// C++23 example (not yet widely supported)std::move_only_function<void()> f = [ptr = std::make_unique<int>(42)]() { /*...*/ };
9. Performance & Best Practices
When to Use Each Tool
| Tool | Use case |
|---|---|
| Raw function pointer | Very lightweight, no capture, fixed signature. |
| Lambda (auto) | Most local uses, best optimisation. |
std::function | Storing heterogeneous callables, callbacks, type erasure needed. |
std::bind | Legacy code or advanced perfect‑forwarding scenarios. |
std::mem_fn | When you need a callable for a member function, especially in generic algorithms. |
std::ref / std::cref | Any time you want to avoid copying into a bind/function. |
| Built‑in functors | For standard operations, often faster than lambdas (compiler knows them). |
Avoid Unnecessary std::function
cpp
// Bad (overhead for no reason)void forEach(const std::vector<int>& v, std::function<void(int)> f) { ... }
// Good (template, zero overhead)template<typename F>void forEach(const std::vector<int>& v, F f) { ... }
Small‑Object Optimisation
std::function typically stores small callables (e.g., a lambda with no captures) inline, avoiding heap allocation. For larger objects, it allocates on the heap.
10. Complete Practical Example – Event System
Let’s tie everything together in a small event dispatcher.
cpp
class EventDispatcher { using Handler = std::function<void(const std::string&, int)>; std::multimap<std::string, Handler> handlers;
public: void subscribe(const std::string& event, Handler h) { handlers.emplace(event, std::move(h)); }
void emit(const std::string& event, int value) { auto range = handlers.equal_range(event); for (auto it = range.first; it != range.second; ++it) { it->second(event, value); } }};
struct Logger { void log(const std::string& ev, int val) const { std::cout << "[Logger] " << ev << " -> " << val << '\n'; }};
int main() { EventDispatcher dispatcher;
// Lambda handler dispatcher.subscribe("click", [](const std::string& e, int v) { std::cout << "Lambda: " << e << " with " << v << '\n'; });
// Free function handler auto freeHandler = [](const std::string& e, int v) { std::cout << "Free: " << e << " = " << v << '\n'; }; dispatcher.subscribe("update", freeHandler);
// Member function using bind and ref Logger logger; dispatcher.subscribe("log", std::bind(&Logger::log, &logger, std::placeholders::_1, std::placeholders::_2));
// Emit events dispatcher.emit("click", 42); dispatcher.emit("update", 100); dispatcher.emit("log", 99);}
Output:
text
Lambda: click with 42Free: update = 100[Logger] log -> 99
Conclusion
#include <functional> is an essential tool for modern C++ developers. It provides:
std::functionfor type‑erased callables,std::bindand placeholders for argument adaptation,std::ref/std::creffor reference semantics,std::mem_fnfor member function pointers,Convenient function objects for standard operations.
While lambdas have replaced many uses of std::bind, the <functional> header remains indispensable for writing flexible, generic, and expressive C++ code. Use it wisely, and your callbacks, algorithms, and event systems will become both powerful and clear.
Now go ahead – #include <functional> and unleash the full potential of callable objects in C++!