First Class Functions

Learn about first-class functions in C++: a feature that lets you store functions in variables, pass them to other functions, and return them, opening up new design possibilities
This lesson is part of the course:

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Free, Unlimited Access
Abstract art representing computer programming
Ryan McCombe
Ryan McCombe
Updated

Much of the content of this series relates to a programming concept known as first-class functions.

A programming language supports first-class functions if it allows functions to be treated like any other type of data. For example:

  • Functions can be stored as variables, class members, or within a container such as an array
  • Functions can be passed as arguments to other functions
  • Functions can be returned from other functions

C++ supports first-class functions in several different ways. In this chapter, we will cover the three main options we have: function pointers, function objects, and lambdas

But first, let's see a scenario where first-class functions are a useful feature

A Motivating Example

Let’s imagine we have a Party class, which is what enables our Player objects to group up and take on bigger challenges.

class Player {
 public:
  bool isAlive() const { return true; }
};

class Party {
 public:
  Player PlayerOne;
  Player PlayerTwo;
  Player PlayerThree;
};

Elsewhere in our code, we’ll want to find out more information about this party. For example, we might want to check that everyone is alive:

#include <iostream>

class Player {/*...*/};
class Party {/*...*/}; int main() { Party MyParty; bool isEveryoneAlive { MyParty.PlayerOne.isAlive() && MyParty.PlayerTwo.isAlive() && MyParty.PlayerThree.isAlive() }; if (isEveryoneAlive) { std::cout << "Everyone is alive"; } }
Everyone is alive

Aside from requiring quite a lot of code, this implementation also makes a big assumption - it assumes the party will always have exactly 3 members. In reality, there may be fewer, and we’d want to manage which Player slots are used within the Party class, rather than making external code responsible.

Worse, if we later expand our Party class to allow additional characters, code like this may still compile, but return the incorrect result because it’s not checking if the new PlayerFour is alive.

The root cause here is that our Party isn’t encapsulated properly. External code shouldn’t be able to poke around the internal workings of our objects like this.

The Party class should take care of details like iterating over its members, and simply expose a friendly public method to access that behaviour.

We covered the importance of encapsulation, and how to implement it, in our beginner course:

Using Class Methods

Lets implement this isEveryoneAlive() function as a class method. Let's also make all of our Player members private to make our interface simpler.

Then, our code can just call MyParty.isEveryoneAlive():

#include <iostream>

class Player {/*...*/}; class Party { public: bool isEveryoneAlive() { return PlayerOne.isAlive() && PlayerTwo.isAlive() && PlayerThree.isAlive(); } private: Player PlayerOne; Player PlayerTwo; Player PlayerThree; }; int main() { Party MyParty; if (MyParty.isEveryoneAlive()) { std::cout << "Everyone is alive"; } }
Everyone is alive

That is better, but we’ve solved this problem in a very specific way - we’re only allowing users of our class to determine if everyone is alive. There are many possible questions people will want to ask of our party.

For example, the outside world might need to know if everyone in the party is online, or above a specific level, or to check if our party has someone who can fill a specifc role:

MyParty.isEveryoneOnline();
MyParty.isEveryoneAtLeastLevel(50);
MyParty.isAnyoneAHealer();

We can’t predict everything people might want to use our class for, and we shouldn’t need to write code for all of those anyway. Thankfully, this is one of the problems first-class functions were designed to solve.

Using Predicates

To solve this problem more flexibly, let's first create a standalone function that implements the check we want to do on each member of the party:

bool PlayerIsAlive(const Player& P) {
  return P.isAlive();
}

A function like this that receives an argument, and returns a boolean representing whether or not that argument satisfies a specific condition is sometimes called a predicate. In this example, our predicate is met if the Player we pass as an argument is alive.

To run this check on everyone in the party, we’d like some way to pass this function to a method on the Party class. This method could then invoke this function for every Player in our Party, and return true if every invocation of this predicate returned true.

For example, a method that checks if every Player meets some condition could be called all_of() and, to check if everyone in our Party is alive, the expression might look something like MyParty.all_of(PlayerIsAlive):

#include <iostream>

class Player {/*...*/}; class Party { public: bool all_of(auto Predicate) { return Predicate(PlayerOne) && Predicate(PlayerTwo) && Predicate(PlayerThree); }
private: }; bool PlayerIsAlive(const Player& P) { return P.isAlive(); } int main() { Party MyParty; if (MyParty.all_of(PlayerIsAlive)) { std::cout << "Everyone is alive"; } }
Everyone is alive

The previous example was accomplished using a function pointer, which we cover in more detail in the next lesson.

The beauty of this design is that we can now ask anything of our Party, without the class needing to be expanded or modified.

The outside world provides the function they want to call, whilst our class can handle the iteration, ensuring that function is called for every Player it is managing:

#include <iostream>

class Player {/*...*/};
class Party {/*...*/}; bool PlayerIsAlive(const Player& P) { return P.isAlive(); } bool PlayerIsOnline(const Player& P) { return P.isOnline(); } bool MinLevel50(const Player& P) { return P.GetLevel() >= 50; } int main() { Party MyParty; if (MyParty.all_of(PlayerIsAlive)) { std::cout << "Everyone is alive"; } if (MyParty.all_of(PlayerIsOnline)) { std::cout << "\nEveryone is online"; } if (!MyParty.all_of(MinLevel50)) { std::cout << "\nNot everyone is level 50+"; } }
Everyone is alive
Everyone is online
Not everyone is level 50+

Callbacks

A function that is provided to another function in this way is sometimes referred to as a callback. In our previous example, PlayerIsAlive(), PlayerIsOnline() and MinLevel50() are all used as callbacks.

They’re provided to another function - the all_of() method in this case. For all_of() to complete its work and generate its return value, it uses our callbacks as needed.

Over the coming lessons, we will see more scenarios where we can apply designs like this and some additional ways they can be implemented in C++

In this lesson, we used function pointers, which are only one of several possibilities. We cover function pointers in more detail in the next lesson.

Summary

In this lesson, we introduced the concept of first-class functions, and one of the ways in which C++ allows us to implement this design. The key things we learned include:

  • First-class functions allow functions to be stored as variables, passed as arguments, and returned from other functions.
  • C++ supports first-class functions in several ways, including function pointers.
  • Predicates are functions that return a boolean value based on whether a condition is met.
  • The practical example using an all_of() method demonstrated some of the flexibility that first-class functions can provide.

Was this lesson useful?

Next Lesson

Function Pointers

Learn about function pointers: what they are, how to declare them, and their use in making our code more flexible
Abstract art representing computer programming
Ryan McCombe
Ryan McCombe
Updated
Lesson Contents

First Class Functions

Learn about first-class functions in C++: a feature that lets you store functions in variables, pass them to other functions, and return them, opening up new design possibilities

A computer programmer
This lesson is part of the course:

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Free, Unlimited Access
Working with Functions
A computer programmer
This lesson is part of the course:

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Free, unlimited access

This course includes:

  • 125 Lessons
  • 550+ Code Samples
  • 96% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Next Lesson

Function Pointers

Learn about function pointers: what they are, how to declare them, and their use in making our code more flexible
Abstract art representing computer programming
Contact|Privacy Policy|Terms of Use
Copyright © 2024 - All Rights Reserved