Functors & Lambda Expressions
Part of the Object Oriented Programming section of Coddy's C++ journey — lesson 74 of 104.
Many STL algorithms accept a callable object that customizes their behavior. You've already seen lambdas used with std::transform. Let's explore both functors and lambda expressions - two ways to create callable objects in C++.
A functor (function object) is a class that overloads the operator(), allowing instances to be called like functions:
#include <algorithm>
#include <vector>
#include <iostream>
struct MultiplyBy {
int factor;
MultiplyBy(int f) : factor(f) {}
int operator()(int x) const {
return x * factor;
}
};
int main() {
std::vector<int> nums = {1, 2, 3, 4};
std::vector<int> result(nums.size());
std::transform(nums.begin(), nums.end(), result.begin(), MultiplyBy(3));
// result: {3, 6, 9, 12}
}Functors can maintain state between calls, which regular functions cannot. However, defining a class for simple operations is verbose. Lambda expressions provide a concise alternative:
int factor = 3;
std::transform(nums.begin(), nums.end(), result.begin(),
[factor](int x) { return x * factor; });The lambda syntax is [capture](parameters) { body }. The capture clause specifies which outside variables the lambda can access. Use [=] to capture all by value, [&] to capture all by reference, or list specific variables like [factor] or [&factor].
Lambdas are particularly useful for one-off operations with algorithms like sorting with custom criteria:
std::vector<int> nums = {5, -2, 8, -1};
std::sort(nums.begin(), nums.end(),
[](int a, int b) { return std::abs(a) < std::abs(b); });
// Sorted by absolute value: {-1, -2, 5, 8}Challenge
EasyLet's build a price calculator that demonstrates both functors and lambda expressions for applying different discount strategies to product prices.
You'll organize your code across two files:
Discounts.h: Define your discount functors and utility functions here.Create a functor called
PercentageDiscountthat stores a discount percentage (as an integer). Itsoperator()should take adoubleprice and return the discounted price. For example, a 20% discount on $100 should return $80.Create another functor called
FixedDiscountthat stores a fixed amount to subtract (as adouble). Itsoperator()should take a price and return the price minus the fixed amount (but never below 0).Create a function called
printPricesthat takes aconst std::vector<double>&and prints all prices separated by spaces, followed by a newline. Format each price with two decimal places.main.cpp: Read five inputs (each on a separate line):- First product price (double)
- Second product price (double)
- Third product price (double)
- Percentage discount to apply (integer, e.g., 20 for 20%)
- Fixed discount amount (double)
Create a vector with the three prices and demonstrate both approaches:
- Print
Original prices:followed by the prices - Use
std::transformwith yourPercentageDiscountfunctor to create a new vector of discounted prices. PrintAfter percentage discount:followed by the results - Use
std::transformwith yourFixedDiscountfunctor on the original prices to create another vector. PrintAfter fixed discount:followed by the results - Use
std::transformwith a lambda expression that doubles each original price. PrintPremium prices (doubled):followed by the results - Use
std::sortwith a lambda to sort the original prices in descending order. PrintSorted (high to low):followed by the sorted prices
For example, with inputs 100.00, 50.00, 75.00, 20, and 15.00:
Original prices: 100.00 50.00 75.00
After percentage discount: 80.00 40.00 60.00
After fixed discount: 85.00 35.00 60.00
Premium prices (doubled): 200.00 100.00 150.00
Sorted (high to low): 100.00 75.00 50.00 This challenge lets you compare functors (which maintain state like the discount amount) with lambdas (which capture variables for quick, inline operations). Both approaches work seamlessly with STL algorithms like std::transform and std::sort.
Cheat sheet
STL algorithms accept callable objects to customize their behavior. Two common approaches are functors and lambda expressions.
A functor is a class that overloads operator(), allowing instances to be called like functions:
struct MultiplyBy {
int factor;
MultiplyBy(int f) : factor(f) {}
int operator()(int x) const {
return x * factor;
}
};
std::transform(nums.begin(), nums.end(), result.begin(), MultiplyBy(3));Functors can maintain state between calls, but require verbose class definitions.
Lambda expressions provide a concise alternative with syntax [capture](parameters) { body }:
int factor = 3;
std::transform(nums.begin(), nums.end(), result.begin(),
[factor](int x) { return x * factor; });The capture clause specifies which outside variables the lambda can access:
[=]- capture all by value[&]- capture all by reference[factor]- capture specific variable by value[&factor]- capture specific variable by reference
Lambdas work well with algorithms like std::sort for custom sorting criteria:
std::sort(nums.begin(), nums.end(),
[](int a, int b) { return std::abs(a) < std::abs(b); });Try it yourself
#include <iostream>
#include <vector>
#include <algorithm>
#include "Discounts.h"
int main() {
// Read inputs
double price1, price2, price3;
int percentageDiscount;
double fixedDiscount;
std::cin >> price1;
std::cin >> price2;
std::cin >> price3;
std::cin >> percentageDiscount;
std::cin >> fixedDiscount;
// Create a vector with the three prices
std::vector<double> prices = {price1, price2, price3};
// TODO: Print "Original prices:" followed by the prices using printPrices
// TODO: Use std::transform with PercentageDiscount functor
// Create a new vector for the results
// Print "After percentage discount:" followed by the results
// TODO: Use std::transform with FixedDiscount functor on original prices
// Create a new vector for the results
// Print "After fixed discount:" followed by the results
// TODO: Use std::transform with a lambda that doubles each original price
// Create a new vector for the results
// Print "Premium prices (doubled):" followed by the results
// TODO: Use std::sort with a lambda to sort original prices in descending order
// Print "Sorted (high to low):" followed by the sorted prices
return 0;
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesC++ Build & CompilationHeader Files & Source FilesNamespaces & ScopeIntroduction to OOP in C++Classes vs ObjectsThe 'this' PointerMethods (Member Functions)Attributes (Data Members)Ctors & Dtors BasicsRecap - Simple Calculator4Class Properties
Instance vs Static MembersGetters and SettersConst Member FunctionsMutable KeywordStatic Methods and VariablesFriend Functions & ClassesRecap - Bank Account Manager7Inheritance
Basic InheritanceInheritance Access LevelsCtor & Dtor Call OrderMethod OverridingVirtual Functions & VTableMultiple InheritanceVirtual InheritanceRecap - Employee Hierarchy10STL Overview
STL Overview & PhilosophySTL ContainersIteratorsSTL AlgorithmsFunctors & Lambda ExpressionsRecap - Word Frequency2Memory Management
Stack vs Heap MemoryPointers and ReferencesDynamic Memory (new/delete)Smart Pointers in C++RAII in C++Recap - Dynamic Array Manager5Encapsulation
Access Specifiers in C++Access Specifiers In DepthInformation HidingStruct vs ClassNested & Inner ClassesRecap - Student Records System8Polymorphism
Compile vs Runtime PolymorphFunction OverloadingVirtual Functions RevisitedPure Virtual FunctionsAbstract ClassesInterface Design in C++Dynamic Casting & RTTIRecap - Shape Calculator3Constructors & Destructors
Default ConstructorParameterized ConstructorCopy ConstructorMove ConstructorConstructor Init ListsDelegating ConstructorsDestructor Deep DiveRule of Three / Five / ZeroRecap - String Class6Operator Overloading
Intro to Operator OverloadArithmetic Operator OverloadComparison Operator OverloadStream OperatorsAssignment Operator Overload[] and () Operator OverloadType Conversion OperatorsRecap - Matrix Class9Templates
Function TemplatesClass TemplatesTemplate SpecializationVariadic TemplatesSFINAE & Type Traits BasicsRecap - Generic Container