🎭 The Magic Helpers: C++ Callable Objects
Imagine you have a magic box. You put something in, press a button, and something happens! That’s what callable objects are in C++. They’re special things you can “call” like magic buttons!
🧙♂️ Meet Our Three Magic Helpers
Think of your code as a kitchen. You need helpers to do tasks:
| Helper | Real-World Analogy |
|---|---|
| Functor | A robot chef with memory |
| Predicate | A taste-tester (yes/no answers) |
| std::function | A universal remote control |
🤖 Functors: Robot Chefs with Memory
What’s a Functor?
A functor is like a robot that:
- Remembers things (stores data)
- Does things when you “call” it
It’s a class that acts like a function!
The Secret: operator()
class Greeter {
string name;
public:
Greeter(string n) : name(n) {}
void operator()() {
cout << "Hello, " << name;
}
};
// Using it:
Greeter robot("Alice");
robot(); // Prints: Hello, Alice
Magic moment: robot() looks like a function call, but it’s actually an object!
Why Functors are Awesome
graph TD A["Regular Function"] --> B["No Memory"] C["Functor Object"] --> D["Has Memory!"] D --> E["Counts things"] D --> F["Stores settings"] D --> G["Remembers history"]
Real Example: A Counter
class Counter {
int count = 0;
public:
int operator()(int x) {
count++;
return x * 2;
}
int getCount() {
return count;
}
};
Counter doubler;
doubler(5); // Returns 10
doubler(3); // Returns 6
cout << doubler.getCount(); // 2
The functor remembers how many times you called it!
✅❌ Predicates: The Yes/No Helpers
What’s a Predicate?
A predicate answers one question: YES or NO?
It’s any callable that returns true or false.
Simple Predicate Function
bool isEven(int n) {
return n % 2 == 0;
}
// Usage with STL:
vector<int> nums = {1,2,3,4,5,6};
auto it = find_if(
nums.begin(),
nums.end(),
isEven
);
// Finds 2 (first even number)
Predicate Functor (with Memory!)
class BiggerThan {
int threshold;
public:
BiggerThan(int t) : threshold(t) {}
bool operator()(int x) {
return x > threshold;
}
};
// Find first number > 10
BiggerThan checker(10);
auto it = find_if(
nums.begin(),
nums.end(),
checker
);
Where Predicates Shine
graph TD A["STL Algorithms"] --> B["find_if"] A --> C["count_if"] A --> D["remove_if"] A --> E["sort with custom compare"] A --> F["copy_if"]
Sorting with Predicates
// Sort descending
sort(nums.begin(), nums.end(),
[](int a, int b) {
return a > b;
}
);
// Or use a functor:
class Descending {
public:
bool operator()(int a, int b) {
return a > b;
}
};
sort(nums.begin(), nums.end(),
Descending());
🎮 std::function: The Universal Remote
The Problem
Different callable types can’t mix easily:
// Function pointer
void (*fp)(int) = someFunc;
// Functor
MyFunctor func;
// Lambda
auto lamb = [](int x){};
// They're all different types! 😫
The Solution: std::function
std::function is a universal container for anything callable!
#include <functional>
// One type fits all!
function<void(int)> action;
// Store a regular function
void greet(int x) {
cout << "Hi " << x;
}
action = greet;
action(5); // Hi 5
// Store a lambda
action = [](int x) {
cout << "Hey " << x;
};
action(5); // Hey 5
// Store a functor
struct Shout {
void operator()(int x) {
cout << "YO! " << x;
}
};
action = Shout();
action(5); // YO! 5
Understanding the Syntax
function<ReturnType(Param1, Param2, ...)>
| You Want | You Write |
|---|---|
| No params, no return | function<void()> |
| Takes int, returns int | function<int(int)> |
| Takes two strings, returns bool | function<bool(string,string)> |
Real Power: Callbacks
class Button {
function<void()> onClick;
public:
void setAction(function<void()> f) {
onClick = f;
}
void click() {
if(onClick) onClick();
}
};
Button btn;
btn.setAction([]() {
cout << "Clicked!";
});
btn.click(); // Clicked!
🎯 Putting It All Together
The Big Picture
graph TD A["Callable Objects"] --> B["Functions"] A --> C["Functors"] A --> D["Lambdas"] B --> E["std::function"] C --> E D --> E E --> F["Store anywhere!"] E --> G["Pass as argument!"] E --> H["Return from function!"]
Complete Example
#include <functional>
#include <vector>
#include <algorithm>
// Predicate functor
class InRange {
int low, high;
public:
InRange(int l, int h)
: low(l), high(h) {}
bool operator()(int x) {
return x >= low && x <= high;
}
};
// Using std::function
void process(
vector<int>& v,
function<bool(int)> pred
) {
for(int x : v) {
if(pred(x))
cout << x << " ";
}
}
int main() {
vector<int> nums = {1,5,10,15,20};
// Pass functor
process(nums, InRange(5, 15));
// Output: 5 10 15
// Pass lambda
process(nums, [](int x) {
return x > 10;
});
// Output: 15 20
}
💡 Quick Tips
| Situation | Use This |
|---|---|
| Need to remember state | Functor |
| Just yes/no for STL | Predicate |
| Store any callable type | std::function |
| One-time simple task | Lambda |
🚀 You Did It!
Now you understand:
- ✅ Functors = Objects that act like functions (with memory!)
- ✅ Predicates = Yes/no helpers for algorithms
- ✅ std::function = Universal container for all callables
You’re ready to write flexible, powerful C++ code! 🎉
