Passing a function as an argument allows a program to change behavior dynamically at runtime. Such a function is called a callback and is commonly used to customize operations like sorting, searching, and event handling.
- Callbacks make code reusable and flexible by passing behavior as an argument.
- Modern C++ also supports callbacks using function wrappers (such as std::function).
#include <iostream>
using namespace std;
// Callback function
int square(int x) {
return x * x;
}
// Function that takes another function as parameter
void process(int value, int (*func)(int)) {
cout << "Result: " << func(value);
}
int main() {
process(5, square); // Passing function as argument
return 0;
}
Output
Result: 25
Explanation
- "square()" is a normal function that performs a task.
- "process()" receives another function as a parameter.
- square is passed as a callback and executed inside "process()"
Syntax
A function can also be passed to another function by passing its address to that function, In simple terms, it could be achieved via pointers.
return_type (*pointer_name)(prm1, prm2)
- return_type: data type of the value that the function returns.
- pointer_name: Name of the function pointer.
- prm1, prm2: Parameters that function accepts.
Ways to Pass Functions in C++
Functions can be passed in multiple ways depending on the use case.
1. Passing a Function Pointer
#include <iostream>
using namespace std;
int add(int x, int y) { return x + y; }
int mul(int x, int y) { return x * y; }
// Function invoke takes a
// pointer to the function
int invoke(int x, int y, int
(*f)(int, int)) {
return f(x, y);
}
int main() {
// Pass address of add & mul
// function
cout << invoke(20, 10, &add) << '\n';
cout << invoke(20, 10, &mul);
return 0;
}
Output
30 200
Explanation
- invoke() accepts a function pointer.
- The address of either add() or mul() is passed.
- The same function executes different logic depending on the callback supplied.
2. Using std::function
Since C++11, the Standard Library provides std::function, which can store any callable object.
Syntax
function< rtype_type (prm1, prm2)> function_name
#include <bits/stdc++.h>
using namespace std;
int add(int x, int y) { return x + y; }
int mul(int x, int y) { return x * y; }
// Function that takes
// function as argument
int invoke(int x, int y,
function<int(int, int)> f) {
return f(x, y);
}
int main() {
// Pass address of add & mul
// function
cout << invoke(20, 10, &add) << '\n';
cout << invoke(20, 10, &mul);
return 0;
}
Output
30 200
Explanation
- std::function wraps callable objects.
- It can accept normal functions, lambdas, functors, and bound member functions.
- It provides greater flexibility than raw function pointers.
3. Using Lambda Expressions
Lambda expressions provide inline anonymous functions that can be passed directly as arguments.
#include <functional>
#include <iostream>
using namespace std;
// Function that takes a pointer
// to a function
int invoke(int x, int y,
function<int(int, int)> func) {
return func(x, y);
}
int main() {
// Define lambdas for addition
// operation and another
// function as a parameter
int k = invoke(20, 10,
[](int x,
int y) -> int {
return x + y;
});
cout << k;
return 0;
}
Output
30
Explanation
- The lambda performs addition.
- It is passed directly without creating a separate function.
- This keeps callback code short and readable.
4. Passing Member Functions
Both static and non-static member functions can be used as callbacks.
- Static member functions behave like normal functions.
- Non-static member functions require an object and are usually passed using std::bind or a lambda.
#include <bits/stdc++.h>
using namespace std;
class C {
public:
int f(int a, int b) {
return a * b;
}
};
void invoke(function<int(int, int)> calc) {
// Call the member function
// using function
cout << calc(10, 20);
}
int main() {
C c;
// Wrapping member function of C
auto calc = bind(&C::f, &c,
placeholders::_1,
placeholders::_2);
// Passing it as argument
invoke(calc);
return 0;
}
Output
200
Explanation
- bind() associates the member function with object c.
- The returned callable object behaves like a normal function.
- nvoke() calls the bound function and prints the result.
Best Practices
Follow these practices while passing functions as parameters:
- Prefer std::function when different callable objects must be supported.
- Use lambda expressions for short, one-time callbacks.
- Use function pointers for lightweight callbacks when flexibility is unnecessary.
- Avoid unnecessary use of std::bind; lambdas are often simpler and easier to read.
Function Pointers vs std::function
| Feature | Function Pointer | std::function |
|---|---|---|
| Type Safety | Not type-safe. Type must match exactly between declaration and usage. | Type-safe; ensures correct signature of callable objects. |
| Flexibility | Less flexible. Only supports function pointers. | More flexible. Supports function pointers, lambdas, and functors. |
| Declaration | Requires explicit declaration of function signature. | Easier to use; can wrap any callable type. |
| Memory Management | No built-in memory management. | Manages memory automatically, including capturing lambdas. |
| Storing State | Cannot store state. | Can store state in addition to functions (e.g., lambdas or functors with state). |
| Use Case | Simple function calls and callbacks. | When you need to store, pass, or return any callable object. |