Appearance
Function Pointers, Functors, and std::function
Video: Function pointers, typedef a function pointer, and std::function | Modern Cpp Series Ep. 30
Overview
C++ provides multiple ways to work with functions as first-class citizens. Understanding the differences between function pointers, functors, and std::function is crucial for choosing the right approach for different scenarios. Each has its own trade-offs in terms of performance, flexibility, and ease of use.
Function Pointers
Function pointers are the most basic way to store and call functions dynamically. They store the memory address of a function.
Basic Syntax
cpp
// Function declaration
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
// Function pointer declaration
int (*operation)(int, int);
// Assign function to pointer
operation = add;
int result = operation(5, 3); // result = 8
// Reassign to different function
operation = subtract;
result = operation(5, 3); // result = 2Function Pointer Types
As you can see, function pointers have a type of R(*)(P1, P2, ...) where R is the return type and P1, P2, ... are the parameter types.
cpp
// Different function signatures
int (*func1)(); // No parameters, returns int
void (*func2)(int); // Takes int, returns void
double (*func3)(int, double); // Takes int and double, returns double
bool (*func4)(const std::string&); // Takes const string reference, returns bool
// Using typedef for cleaner syntax
typedef int (*OperationFunc)(int, int);
OperationFunc op = add;
// C++11 using alias
using OperationFunc = int (*)(int, int);Function Pointers as Parameters
cpp
// Function that takes a function pointer
int applyOperation(int a, int b, int (*operation)(int, int)) {
return operation(a, b);
}
// Usage
int result = applyOperation(10, 5, add); // result = 15
result = applyOperation(10, 5, subtract); // result = 5Function Pointers in Arrays
cpp
// Array of function pointers
int (*operations)(int, int) = {add, subtract};
// Call functions from array
int result1 = operations[0](10, 5); // add(10, 5) = 15
int result2 = operations[1](10, 5); // subtract(10, 5) = 5Functors (Function Objects)
Functors are classes that overload the operator() method, making instances callable like functions. They can maintain state between calls and provide more flexibility than function pointers.
Basic Functor
cpp
class Adder {
private:
int increment;
public:
Adder(int inc) : increment(inc) {}
int operator()(int value) const {
return value + increment;
}
};
// Usage
Adder addFive(5);
int result = addFive(10); // result = 15Stateful Functors
cpp
class Counter {
private:
int count;
public:
Counter() : count(0) {}
int operator()() {
return ++count;
}
void reset() { count = 0; }
int getCount() const { return count; }
};
// Usage
Counter counter;
int first = counter(); // first = 1
int second = counter(); // second = 2
int third = counter(); // third = 3Functors vs Function Pointers
Advantages of Functors:
- Can maintain state between calls
- Can be inlined by the compiler (better performance)
- Can have multiple overloads
- Can have additional methods
Advantages of Function Pointers:
- Simpler syntax
- No object creation overhead
- Can be stored in C-style arrays
std::function
std::function is a template class that provides type erasure for callable objects. It can store functions, functors, lambdaslambdas are a shorthand for creating functors, and member functionsmember functions are functions that are part of a class, they can be stored in std::function as well! with the same signature.
Basic Usage
cpp
#include <functional>
// Function
int add(int a, int b) { return a + b; }
// Functor
class Multiplier {
public:
int operator()(int a, int b) const { return a * b; }
};
// Lambda
auto subtract = (int a, int b) { return a - b; };
// Store different callables
std::function<int(int, int)> operation;
operation = add; // Function
int result1 = operation(5, 3); // result1 = 8
operation = Multiplier(); // Functor
int result2 = operation(5, 3); // result2 = 15
operation = subtract; // Lambda
int result3 = operation(5, 3); // result3 = 2std::function as Parameters
cpp
// Function that accepts any callable with the right signature
int processNumbers(int a, int b, std::function<int(int, int)> operation) {
return operation(a, b);
}
// Can pass different types of callables
int result1 = processNumbers(10, 5, add); // Function
int result2 = processNumbers(10, 5, Multiplier()); // Functor
int result3 = processNumbers(10, 5, subtract); // LambdaPerformance Comparison
Function Pointers
- Zero overhead: Direct function call
- Fastest: No indirection or type checking
- Limited: Can only store function addresses
Functors
- Very fast: Can be inlined by compiler
- Stateful: Can maintain data between calls
- Template-friendly: Work well with generic code
std::function
- Some overhead: Type erasure and virtual calls.
std::functionneeds to store the type of the callable and a pointer to it, which causes an extra indirection. This can be a performance bottleneck if you are not careful. - Flexible: Can store any callable object
- Type-safe: Compile-time signature checking
When to Use Each
Use Function Pointers When:
- Performance is critical
- You only need to store function addresses
- Working with C-style APIs
- Memory overhead must be minimal
cpp
// Performance-critical code
void processArray(int* data, int size, int (*processor)(int)) {
for (int i = 0; i < size; i++) {
data[i] = processor(data[i]);
}
}Use Functors When:
- You need to maintain state between calls
- Performance is important
- Working with templates
- You want to provide additional functionality
cpp
// Stateful operations
class Accumulator {
int sum = 0;
public:
int operator()(int value) { return sum += value; }
void reset() { sum = 0; }
int getSum() const { return sum; }
};Use std::function When:
- You need to store different types of callables
- Type erasure is required
- Flexibility is more important than performance
- Working with callbacks or event systems
cpp
// Flexible callback system
class EventHandler {
std::function<void(int)> callback;
public:
void setCallback(std::function<void(int)> cb) { callback = cb; }
void trigger(int value) { if (callback) callback(value); }
};Common Patterns
Strategy Pattern
cpp
class Sorter {
private:
std::function<bool(int, int)> comparator;
public:
Sorter(std::function<bool(int, int)> comp) : comparator(comp) {}
void sort(std::vector<int>& data) {
std::sort(data.begin(), data.end(), comparator);
}
};
// Usage
Sorter ascending((int a, int b) { return a < b; });
Sorter descending((int a, int b) { return a > b; });Command Pattern
cpp
class Command {
public:
virtual ~Command() = default;
virtual void execute() = 0;
};
class CommandExecutor {
private:
std::vector<std::function<void()>> commands;
public:
void addCommand(std::function<void()> cmd) {
commands.push_back(cmd);
}
void executeAll() {
for (auto& cmd : commands) {
cmd();
}
}
};Best Practices
1. Choose the Right Tool
cpp
// Use function pointers for simple cases
int (*simpleFunc)(int) = (int x) { return x * 2; };
// Use functors for stateful operations
class StatefulFunctor { /* ... */ };
// Use std::function for flexibility
std::function<int(int)> flexibleFunc = (int x) { return x * 2; };2. Consider Performance
cpp
// Fast: Function pointer
void fastProcess(int (*func)(int)) { /* ... */ }
// Fast: Functor (can be inlined)
template<typename Func>
void fastProcess(Func func) { /* ... */ }
// Slower: std::function (type erasure overhead)
void slowProcess(std::function<int(int)> func) { /* ... */ }3. Use const Correctness
cpp
// Function pointer
int (*func)(int) const;
// Functor
class Functor {
public:
int operator()(int x) const; // Mark as const when possible
};
// std::function
std::function<int(int)> func;Questions
Q: What is the main advantage of std::function over function pointers?
std::function provides type erasure and can store any callable object with the same signature, including functions, lambdas, member functions, and functors. Function pointers can only store function addresses.
Q: What is a functor in C++?
A functor is a class that overloads the operator() method, making instances of the class callable like functions. This allows them to maintain state between calls.
Q: When should you prefer function pointers over std::function?
Function pointers have zero overhead and are faster than std::function, which has some overhead due to type erasure. Use function pointers when you only need to store functions and performance is critical.
Q: What is the primary purpose of std::function?
std::function provides type erasure, allowing you to store and call different types of callable objects (functions, lambdas, functors) with the same signature in a uniform way.
Q: How do you declare a function pointer to a function that takes two ints and returns a bool?
The correct syntax is 'bool (*func)(int, int);' where the parentheses around *func are necessary to indicate that func is a pointer to a function, not a function that returns a pointer.