Lesson 1
Storing a Function in a Variable
Lesson Introduction

Hello and welcome! Today's lesson explores a powerful feature of C++: storing functions in variables. This is useful for scenarios like callbacks, event handlers, or flexible program design. Our goal is to understand how to do this using C++ via function pointers and std::function.

By the end of this lesson, you'll know how to store functions in variables using both traditional function pointers and the modern std::function type.

Most importantly, we will learn to operate functions as objects, which will be very useful in future lessons.

Introduction to Function Pointers

First, let's look at function pointers, a classic C++ feature. A function pointer stores the address of a function, allowing dynamic behavior.

Consider a function to add two integers:

C++
1int add(int a, int b) { 2 return a + b; 3}

To store this function in a pointer:

C++
1#include <iostream> 2 3int add(int a, int b) { 4 return a + b; 5} 6 7int main() { 8 // Declare a function pointer 9 int (*fp)(int, int) = add; 10 11 // Use the function pointer 12 std::cout << "Using function pointer: " << fp(2, 3) << '\n'; // Output: Using function pointer: 5 13 14 return 0; 15}

Here, int (*fp)(int, int) = add; declares a function pointer fp that can point to any function taking two ints and returning an int. We then call fp(2, 3) to use the add function through the pointer.

Function pointers are also function objects because they can be called like ordinary functions. A function object in functional programming is essentially a function treated as a value, similar to how you treat numbers or strings. It means you can pass these functions around as arguments to other functions, return them as results from other functions, and assign them to variables.

Additionally, all types that can be implicitly converted to a function pointer are function objects, but this should be avoided. You should prefer proper function objects because they’re more powerful and easier to handle.

std::function Overview

Next, we introduce std::function, from the <functional> header. std::function is versatile and safer than raw function pointers, able to store any callable target.

Here's a comparison using std::function for the add function:

C++
1#include <iostream> 2#include <functional> 3 4int add(int a, int b) { 5 return a + b; 6} 7 8int main() { 9 // Using std::function 10 std::function<int(int, int)> func = add; 11 12 std::cout << "Using std::function: " << func(2, 3) << '\n'; // Output: Using std::function: 5 13 14 return 0; 15}

Here, std::function<int(int, int)> func = add; stores the add function in a std::function object, which improves type safety and flexibility.

  1. std::function: std::function<int(int, int)> func = add; creates a std::function object storing any callable entity with the int(int, int) signature.
  2. Using std::function: std::cout << "Using std::function: " << func(2, 3) << '\n'; calls add via the std::function object func, yielding the desired result: 5.

std::function offers more flexibility and ease for storing various callable objects. However, it’s crucial to note that std::function can introduce noticeable performance penalties. To be able to hide the contained type and provide a common interface over all callable types, std::function uses a technique known as type erasure. Type erasure is usually based on virtual member function calls. Because virtual calls are resolved at runtime, the compiler can’t inline the call and has limited optimization opportunities.

Pros and Cons: Function Pointers

Pros:

  • Direct and simple method.
  • Performance overhead is minimal.

Cons:

  • Less type-safe.
  • Limited flexibility in handling different callable entities.
Pros and Cons: `std::function`

Pros:

  • Provides a versatile, type-safe way to store various callable entities.
  • Can store functions, lambda expressions, and functors.

Cons:

  • Performance overhead due to type erasure.
  • Can obscure what's happening under the hood, making debugging more complex.
Lesson Summary

We explored how to store functions in variables using both function pointers and std::function. We learned:

  • Function pointers: Store function addresses but offer less safety and flexibility. They have minimal performance overhead but are limited in versatility.
  • std::function: Provides a versatile, type-safe way to store various callable entities, though it can introduce performance penalties due to type erasure.

Now that you understand the theory and practical aspects, it's time for practice. In the next set of exercises, you'll try storing and using functions in variables using both methods. This hands-on practice is crucial to solidify your understanding. Let's get started!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.