C++ Functors (Function Objects)

An introduction to functors - how to create objects that behave like functions
This lesson is part of the course:

Professional C++

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

a55.jpg
Ryan McCombe
Ryan McCombe
Posted

Another option we have for implementing first-class functions in C++ is function objects. Function objects are sometimes also referred to as functors.

Creating functors involves operator overloading. We’ve previously seen how we can overload operators like ++ in our custom class code.

The syntax we use to call functions, (), is also an operator, and so can be overloaded in the same way:

class Functor {
public:
  void operator()() const {
    std::cout << "Hello world!";
  }
};

The double set of () here might seem weird, but it is consistent with the syntax we use for other operator overloads. For example, we’d overload ++ like this:

void operator++() {};

So, given we’re overloading the () operator, replacing ++ with () might make more sense.

Now, with our when we have an instance of this object, we can “call” it:

int main() {
  Functor MyFunctor;
  MyFunctor();
}

A common mistake here is to use the () operator with the class, rather than an object. This code will compile, but it is calling the class constructor, rather than our operator overload:

int main() {
  Functor();
}

Given the constructor returns an instance of the object, we could chain an additional () to this expression, which would then call our overload on the object that was constructed:

int main() {
  Functor()();
}

Using Functors as First-Class Functions

Like any object, this can be passed around to other functions, as either a copy or a reference. Therefore, functors allow us to implement the behavior of first-class functions:

void CallIfEven(int n, auto callback) {
  if (n%2 == 0) callback();
}

int main() {
  Functor MyFunctor;
  CallIfEven(2, MyFunctor);
}

Callables

A function object might look a lot like a function, particularly given it is “callable” using the same code. But, it’s not strictly correct to refer to it as a function.

Things that can be “called” (or invoked) in programming languages are often referred to as “callables”. Functions are an example of callables, but not all callables are functions.

Functor Return Values

As with regular functions, we can return values from functors. We do that by replacing the void in our overload to the type we want to return, and then use appropriate return statements:

#include <iostream>

class Functor {
public:
  int operator()() const {
    return 5;
  }
};

int main() {
  Functor MyFunctor;
  std::cout << MyFunctor();
}

Functor Parameters

Within the second set of brackets, we can allow our functors to accept arguments:

#include <iostream>

class Functor {
public:
  int operator()(int x, int y) const {
    return x + y;
  }
};

int main() {
  Functor MyFunctor;
  std::cout << MyFunctor(2, 3);
}

This also means we can overload the () operator multiple times within the same class, as long as our parameter lists are unique:

#include <iostream>

class Functor {
public:
  void operator()() const {
    std::cout << "Hello World\n";
  }
  void operator()(int x) const {
    std::cout << "Hello Integer\n";
  }
};

int main() {
  Functor MyFunctor;
  MyFunctor();
  MyFunctor(5);
}
Hello World
Hello Integer

Functor Benefits

Functors have a lot more power than simple function pointers. Being instances of classes, we naturally have all the power that brings with it. For example:

  • they can have other methods and variables accessible with the . operator
  • they can have constructors
  • they can overload other operators
  • they can exist within an inheritance tree

However, the most common use case for functors is simply when we require a callable with some persistent state.

For example, here, we have a functor that returns how many times it has been called:

class Functor {
public:
  void operator()() {
    std::cout << "I have been called "
              << ++Invocations
              << " time(s)\n";
  }
private:
  int Invocations { 0 };
};

int main() {
  Functor MyFunctor;
  MyFunctor();
  MyFunctor();
  MyFunctor();
}
I have been called 1 time(s)
I have been called 2 time(s)
I have been called 3 time(s)

Implementing similar behavior with a regular function wouldn’t be quite so easy to encapsulate.

Passing Functors by Reference

Remember, as with any object, a functor passed to another function is going to be passed by value by default.

Here, our CallIfEven function is receiving copies of our function object:

void CallIfEven(int n, auto callback) {
  if (n%2 == 0) callback();
}

When our reason for using the functor is to maintain some internal state, this is generally not what we want to happen:

int main() {
  Functor MyFunctor;
  MyFunctor();
  CallIfEven(2, MyFunctor);
  MyFunctor();
}
I have been called 1 time(s)
I have been called 2 time(s)
I have been called 2 time(s)

Instead, we typically want to pass our functors by reference, using the & syntax:

void CallIfEven(int n, auto& callback) {
  if (n%2 == 0) callback();
}
I have been called 1 time(s)
I have been called 2 time(s)
I have been called 3 time(s)

Was this lesson useful?

Ryan McCombe
Ryan McCombe
Posted
This lesson is part of the course:

Professional C++

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

7a.jpg
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:

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

C++ Lambdas

An introduction to Lambdas - a more concise way of creating functions
vb4.jpg
Contact|Privacy Policy|Terms of Use
Copyright © 2023 - All Rights Reserved