std::function
vs Function Pointer Performance
What's the overhead of using std::function
compared to raw function pointers?
std::function
provides more flexibility than raw function pointers but comes with some overhead. Let's examine the differences and when each might be appropriate.
Raw Function Pointers
Function pointers are simple and lightweight:
- Fixed size (typically 8 bytes on 64-bit systems)
- No heap allocation
- Very fast to call (similar to regular function calls)
class Player {
public:
using FnPtr = void(*)(int NewHealth);
void SetDelegate(FnPtr Fn) {
OnDamage = Fn;
}
void TakeDamage(int Damage) {
Health -= Damage;
if (OnDamage) OnDamage(Health);
}
private:
FnPtr OnDamage{nullptr};
int Health{100};
};
void LogHealth(int NewHealth) {
std::cout << "Health: " << NewHealth << '\n';
}
int main() {
Player P;
P.SetDelegate(LogHealth);
P.TakeDamage(30);
}
std::function
std::function
is more flexible but has overhead:
- Variable size (typically 32 bytes for the object itself)
- May require heap allocation for larger callables
- Slightly slower call time due to type erasure
class Player {
public:
using Delegate = std::function<
void(int NewHealth)>;
void SetDelegate(Delegate D) {
OnDamage = D;
}
void TakeDamage(int Damage) {
Health -= Damage;
if (OnDamage) OnDamage(Health);
}
private:
Delegate OnDamage;
int Health{100};
};
int main() {
Player P;
// Can store free functions
P.SetDelegate(LogHealth);
// Can store lambdas with capture
int LogCount{0};
P.SetDelegate([&LogCount](int NewHealth) {
std::cout << "Health: " << NewHealth << '\n';
LogCount++;
});
P.TakeDamage(30);
}
When to Use Each
Use raw function pointers when:
- You only need to store simple function pointers
- Performance is absolutely critical
- Memory usage must be minimized
Use std::function
when:
- You need to store lambdas with captures
- You need to store member functions
- You want to store different types of callables
- The flexibility is worth the small overhead
Remember that premature optimization is the root of all evil. Start with std::function
for its flexibility, and only switch to raw function pointers if profiling shows it's necessary for your specific use case.
Delegates and the Observer Pattern
An overview of the options we have for building flexible notification systems between game components