Callable Objects

Back

Loading concept...

🎭 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:

  1. Remembers things (stores data)
  2. 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! 🎉

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.