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.
- Functions can be passed using function pointers.
- 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()"
1. Passing Pointer to a Function
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.
Syntax:
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.
#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:
- The program defines two functions add and mul and passes them as callbacks to another function "invoke()".
- "invoke()" receives a function pointer (*f)(int, int) and calls it using f(x, y).
- In "main()", add and mul are passed to "invoke()", so the same function runs different logic (addition and multiplication).
2. Using Function Wrapper
In C++ 11, there is a function template class function<> that allows to pass functions as objects.
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 arguemnt
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
Lambdas with Function Wrapper
Lambdas in C++ provide a way to define inline, one-time, anonymous function objects. These lambdas can be defined in a place where it is required to pass a function as an argument.
#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:
- invoke takes two integers and a function (func) as parameters, and calls func(x, y).
- Instead of a named function, a lambda "(int x, int y) -> int { return x + y; }" is passed directly to invoke.
- invoke(20, 10, lambda) runs the lambda, performing addition of 20 + 10.
3. Passing Member Function of a Class
It means providing a class's function as an argument to another function. Non-static member functions need an object to be called, while static member functions can be passed directly like regular functions.
- Use std::bind to associate a non-static member function with an object before passing.
- Lambdas can wrap non-static members for easier passing.
#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:
- "C" has a non-static member function f that multiplies two numbers.
- "std::bind(&C::f, &c, placeholders::_1, placeholders::_2)" creates a callable object linking "f" with the object "c".
- The bound function "calc" is passed to invoke, which calls it like a normal function.
- calc(10, 20) executes c.f(10, 20) and prints 200.
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. |