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.
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.
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 anEmployee
object and returns the new salary by multiplying the current one with the factor.
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);
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 eachEmployee
.
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.
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}
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!