std::function
Master std::function, the polymorphic wrapper that can store any callable object for flexible callback systems
Learn std::function - the polymorphic wrapper that unifies all callable types for building flexible callback systems.
A Simple Example
#include <iostream>
#include <functional>
#include <vector>
// Regular function
int add(int a, int b) {
return a + b;
}
// Functor
struct Multiply {
int operator()(int a, int b) const {
return a * b;
}
};
int main() {
// std::function can hold any callable with signature int(int, int)
std::function<int(int, int)> operation;
// Store function pointer
operation = add;
std::cout << "5 + 3 = " << operation(5, 3) << "\n";
// Store lambda
operation = [](int a, int b) { return a - b; };
std::cout << "5 - 3 = " << operation(5, 3) << "\n";
// Store functor
operation = Multiply{};
std::cout << "5 * 3 = " << operation(5, 3) << "\n";
// Vector of functions
std::vector<std::function<void()>> callbacks;
callbacks.push_back([]() { std::cout << "Callback 1" << "\n"; });
callbacks.push_back([]() { std::cout << "Callback 2" << "\n"; });
std::cout << "\nExecuting callbacks:" << "\n";
for (const auto& callback : callbacks) {
callback();
}
return 0;
}
Breaking It Down
What is std::function?
- What it is: A polymorphic wrapper that can store any callable with a matching signature
-
Syntax:
std::function<ReturnType(Params)>- e.g.,std::function<int(double, char)> - Stores: Function pointers, lambdas, functors, member functions (with std::bind)
- Remember: It erases the specific type but preserves the signature
Type Erasure
- What it does: Hides the actual type of the callable behind a common interface
- How: Uses internal polymorphism (similar to virtual functions)
- Trade-off: Runtime overhead (heap allocation, virtual dispatch) for flexibility
- Remember: Similar to how void* erases pointer types, but type-safe
When to Use std::function
- Runtime selection: When you need to choose which function to call at runtime
- Containers: When storing multiple callables in vectors, maps, etc.
- Callbacks: For event systems, GUI frameworks, async operations
- Remember: Use templates when types are known at compile time for zero overhead
Performance Considerations
- Overhead: Heap allocation for large captures, virtual dispatch for calls
- Copying: std::function copies the wrapped callable
- Alternative: For performance-critical code with known types, use templates
- Remember: Measure if performance matters - often the flexibility is worth it
Why This Matters
- std::function solves a fundamental problem: storing different types of callables (function pointers, lambdas, functors) in a uniform way.
- It's essential for callbacks, event systems, strategy patterns, and any time you need to store or pass around functions as first-class objects.
- Understanding std::function completes your knowledge of modern C++ callable objects.
Critical Insight
std::function uses type erasure - it hides the actual type of the callable behind a common interface, similar to how virtual functions work. This means there's runtime overhead (heap allocation for large captures, virtual dispatch) compared to templates or direct function calls.
But this overhead buys you incredible flexibility - you can store different callable types in containers, change them at runtime, and build sophisticated callback systems. Think of it like this: templates are compile-time polymorphism (fast, but types must be known), std::function is runtime polymorphism (flexible, but has cost).
Use templates when you know types at compile time, std::function when you need runtime flexibility. They solve different problems!
Best Practices
Match signatures exactly: Ensure the std::function signature matches the callable's return type and parameters.
Use for runtime polymorphism: Choose std::function when you need to store different callable types or select at runtime.
Prefer templates for performance: When types are known at compile time, templates avoid std::function's overhead.
Check for empty: Use if (func) to check if std::function is empty before calling to avoid std::bad_function_call.
Common Mistakes
Performance overhead: std::function has runtime cost (heap allocation, virtual dispatch). Use templates for hot paths.
Copying large captures: std::function copies the callable, which can be expensive for lambdas with large captures.
Calling empty std::function: Calling an empty std::function throws std::bad_function_call. Always check with if (func).
Signature mismatches: The std::function template signature must exactly match the callable. Even const differences matter.
Debug Challenge
This std::function has the wrong signature. Click the highlighted line to fix it:
Quick Quiz
- What's the syntax for std::function holding a function that takes int, returns double?
- When should you use std::function instead of templates?
- What is the main cost of using std::function?
Practice Playground
Time to try out what you just learned! Play with the example code below, experiment by making changes and running the code to deepen your understanding.
Output:
Error:
Lesson Progress
- Fix This Code
- Quick Quiz
- Practice Playground - run once