Lesson 4
Advanced Example with Functional Objects
Lesson Introduction

Hello! In our journey through functional programming in C++, we've explored currying, partial application, and functors. Today, we'll dive into an advanced example using functional objects, or functors, in combination with the powerful Boost.Range library. The goal is to deepen your understanding of how to create and utilize functional objects to make your code more modular and reusable, especially in a real-world context like adjusting employee salaries.

By the end of this lesson, you'll be able to create complex functional objects, apply them to collections using Boost.Range, and understand the benefits of such an approach.

Setting Up

We start by defining a struct for employees:

C++
1struct Employee { 2 std::string name; 3 double salary; 4};

This structure holds basic details about an employee, which include their name and salary.

Defining a Functional Object

Let's create a functor that increases salary by a certain factor:

C++
1struct SalaryIncrease { 2 double factor; 3 SalaryIncrease(double f) : factor(f) {} 4 5 double operator()(const Employee& emp) const { 6 return emp.salary * factor; 7 } 8};

In this example:

  • The constructor SalaryIncrease(double f) initializes the factor used to increase the salary.
  • The operator() takes an Employee object and returns the new salary by multiplying the current one with the factor.
Using Functional Objects in a Program

Let's integrate the Employee and SalaryIncrease functor into a simple program. We need a collection of employees. We'll use a std::vector:

C++
1std::vector<Employee> employees = { 2 {"Alice", 30000}, 3 {"Bob", 45000}, 4 {"Charlie", 32000}, 5 {"David", 52000}, 6 {"Eve", 48000} 7};

To apply the 10% salary increase, we instantiate the SalaryIncrease functor:

C++
1double increaseFactor = 1.1; // 10% raise 2SalaryIncrease increase(increaseFactor);
Applying the Functor with Boost.Range

Boost.Range is a powerful library for working with ranges in a way that feels natural and expressive. We'll use it to apply our salary increase functor to each employee in our vector.

We use Boost's transformed adaptor to apply our functor:

C++
1#include <boost/range/adaptor/transformed.hpp> 2#include <boost/range/algorithm/for_each.hpp> 3 4auto increased_salaries = employees | boost::adaptors::transformed([&increase](const Employee& emp) { 5 return Employee{emp.name, increase(emp)}; 6});

In this code:

  • boost::adaptors::transformed applies our functor to each element in the range.
  • The lambda function [&increase](const Employee& emp) captures the functor and applies it to each Employee.
Printing Results with Boost.ForEach

To print the results, we'll use boost::for_each:

C++
1#include <iostream> 2 3boost::for_each(increased_salaries, [](const Employee& emp) { 4 std::cout << emp.name << " now earns: " << emp.salary << "\n"; 5}); 6// Output: 7// Alice now earns: 33000 8// Bob now earns: 49500 9// Charlie now earns: 35200 10// David now earns: 57200 11// Eve now earns: 52800

In this code:

  • boost::for_each iterates over the transformed range.
  • The lambda prints each employee's name and new salary.
Complete Program

Here is the complete program demonstrating all the concepts:

C++
1#include <iostream> 2#include <vector> 3#include <functional> 4#include <boost/range/adaptor/transformed.hpp> 5#include <boost/range/algorithm/for_each.hpp> 6 7struct Employee { 8 std::string name; 9 double salary; 10}; 11 12struct SalaryIncrease { 13 double factor; 14 SalaryIncrease(double f) : factor(f) {} 15 16 double operator()(const Employee& emp) const { 17 return emp.salary * factor; 18 } 19}; 20 21int main() { 22 std::vector<Employee> employees = { 23 {"Alice", 30000}, 24 {"Bob", 45000}, 25 {"Charlie", 32000}, 26 {"David", 52000}, 27 {"Eve", 48000} 28 }; 29 30 double increaseFactor = 1.1; // 10% raise 31 SalaryIncrease increase(increaseFactor); 32 33 auto increased_salaries = employees | boost::adaptors::transformed([&increase](const Employee& emp) { 34 return Employee{emp.name, increase(emp)}; 35 }); 36 37 boost::for_each(increased_salaries, [](const Employee& emp) { 38 std::cout << emp.name << " now earns: " << emp.salary << "\n"; 39 }); 40 // Output: 41 // Alice now earns: 33000 42 // Bob now earns: 49500 43 // Charlie now earns: 35200 44 // David now earns: 57200 45 // Eve now earns: 52800 46 47 return 0; 48}
Lesson Summary and Practice Introduction

To recap, in this lesson we've:

  • Defined and utilized functors in C++ to make our code more modular and reusable.
  • Applied these functors to collections using Boost.Range to create elegant and readable code.
  • Printed the results using boost::for_each.

Now it's time to get hands-on practice. You'll move to practice sessions where you'll apply these concepts using the CodeSignal IDE. This will help solidify your understanding and mastery of creating and using functional objects in C++. Happy coding!

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