Lambdas and Callable Objects

Function Objects

A function object is just a plain object that can be used in the same syntax used to call functions. You typically define a small struct or class that carries the code you want executed. That code is run when you write the object's name followed by parentheses.

The particular syntax that makes this possible has the special name operator(). Here is the smallest possible working example.

#include <iostream>

struct Greeter
{
    void operator()()
    {
        std::cout << "hello\n";
    }
};

int main()
{
    Greeter g{};
    g();
    // Return the computed value to the caller
    return 0;
}

Here, the struct Greeter has a single member function named exactly operator(). In main, a Greeter object named g is created. We can then “call” g just as if it were a function name: g().

Since g is of a type that defines an operator() member function, that function is called, the body executed, and the program outputs hello.

The empty parentheses are required, they cause this special member function to be called.

The same approach works if the function call involves parameters.

#include <iostream>

// Define the members that belong to this type
struct Adder
{
    int operator()(int x, int y)
    {
        return x + y;
    }
};

int main()
{
    Adder add{};
    std::cout << add(5, 7) << '\n';
    return 0;
}

Here we have a type Adder where its operator() takes two integer parameters. In main, we create a type Adder object named add. We then "call" it as if it were an ordinary function: add(5, 7).

This invokes the operator() function of the object add, passing 5 and 7 to the parameters x and y, respectively.

The body executes and returns the sum, which is then printed. Again, the object's interface looks identical to a function, but all the computation is handled by the method belonging to the object.

Just like other objects, function objects can carry their own internal state that remains between calls. We do this just as for other objects: declare data members.

#include <iostream>

struct Counter
{
    int value{ 0 };

    void operator()()
    {
        value = value + 1;
        std::cout << value << '\n';
    }
};

int main()
{
    Counter c{};
    c();
    c();
    c();
    return 0;
}

In the example above, each call to the Counter object will execute the operator() member, which increments the value data member and then prints it. After three calls, the output is 1, 2, 3, indicating that the data member persists between calls.

The significant ability of a function object is its ability to be passed as an argument to algorithms requiring some kind of function to be run (often referred to as a “callable”). For example, the algorithm std::for_each simply applies a callable entity to every element of a sequence. Here is a simple example where the callable prints each item, and keeps track of how many times it has printed.

#include <algorithm>
#include <iostream>
#include <vector>

struct Printer
{
    int printed{ 0 };

    void operator()(int n)
    {
        std::cout << n << ' ';
        printed = printed + 1;
    }
};

int main()
{
    std::vector v{ 10, 20, 30, 40 };
    Printer p{};

    // Call this function with the provided arguments
    std::for_each(v.begin(), v.end(), p);

    std::cout << '\n';
    std::cout << p.printed << '\n';
    return 0;
}

std::for_each iterates through each element in the vector, calling the provided function object for each element. For each integer, p.operator() will first print the integer and then increment p.printed. After the loop completes, the final count stored in p.printed is printed.

Notice that std::for_each creates a copy of the function object to pass to the algorithm; therefore, the original object p remains in its original state of 0 items printed.

The updated count has already been performed at that point, but must either be captured on the return value or shared in some other way if the caller actually needs the post processing value from the object instance.

So to repeat: a function object is a data structure (an object that possesses some associated code). That code is placed in a member function named operator(). 

Objects of that type may then be invoked like normal functions (provided that operator() is implemented correctly) and may also carry their own data. It's the combination of functional calling syntax and encapsulating state within the object that makes them so powerful.

Exercise: Function ObjectsStarter code, hints and solutionOpenClose
Guided practiceBeginner · 10–15 min

Practice: Function Objects

Work through the mission, reveal hints only when needed, and compare your result with the model solution.

Your challenge

Keep the Printer returned by for_each and compare its count with p.printed. Then call p(99) and report p's count again, explaining the copied callable.

Mission checklist

  1. Run the starter as a baseline, then predict the change requested in Your challenge.
  2. Make that change in your editor; use the lesson-specific hints if you need help.
  3. Compile and run your answer, then compare with the worked solution and explain the result.

Starter program

This is the working baseline, not the completed answer. Compile as C++20, then make the change described above.

#include <algorithm>
#include <iostream>
#include <vector>

struct Printer
{
    int printed{ 0 };

    void operator()(int n)
    {
        std::cout << n << ' ';
        printed = printed + 1;
    }
};

int main()
{
    std::vector v{ 10, 20, 30, 40 };
    Printer p{};

    // Call this function with the provided arguments
    std::for_each(v.begin(), v.end(), p);

    std::cout << '\n';
    std::cout << p.printed << '\n';
    return 0;
}

Hints

Hint 1

Printer is callable because it defines operator()(int). std::for_each receives p by value here, so its internal copy counts the printed elements.

Hint 2

Assign the returned callable from for_each to another Printer. p itself is unchanged until you invoke p directly.

Worked solution

for_each receives a copy of p and returns its updated callable. The solution retains that returned Printer with count 4, while original p still has count 0. Calling p(99) afterward updates p itself to count 1.

#include <algorithm>
#include <iostream>
#include <vector>

struct Printer
{
    int printed{ 0 };

    void operator()(int n)
    {
        std::cout << n << ' ';
        printed = printed + 1;
    }
};

int main()
{
    std::vector v{ 10, 20, 30, 40 };
    Printer p{};

    // Call this function with the provided arguments
    Printer result = std::for_each(v.begin(), v.end(), p);

    std::cout << '\n';
    std::cout << p.printed << '\n';
    std::cout << "Returned count: " << result.printed << '\n';
    std::cout << "Original count: " << p.printed << '\n';
    p(99);
    std::cout << "\nAfter direct call: " << p.printed << '\n';
    return 0;
}

Example output

10 20 30 40 
0
Returned count: 4
Original count: 0
99 
After direct call: 1

0 comments

No comments yet. Be the first.

Add a comment

About CppLearn

CppLearn is an interactive, free reference for modern C++. In each lesson, you'll find out how the language works, rather than just lines of code that you can copy and paste. We cover concepts starting from beginner lessons on fundamentals and proceed to templates, move semantics, smart pointers, and areas that usually trap or scare C++ learners.