Functional Programming (FP) is a programming paradigm where functions are treated as first-class citizens, state and data mutation is avoided, and code is written using pure functions, higher-order functions, and immutability.
Although C++ is primarily an imperative and object-oriented language, modern C++ (since C++11) has introduced several features that allow us to use functional programming techniques effectively.
What is Functional Programming?
Functional programming emphasizes:
- Pure Functions: No side effects (no global variables, no I/O)
- Immutability: No variable reassignment or mutation
- First-Class Functions: Functions can be passed around like variables
- Higher-Order Functions: Functions that take other functions as arguments or return them
- Recursion: Loops are often replaced with recursive functions
- Declarative Style: Focuses on “what to do”, not “how to do it”
Key Functional Features in C++
C++ supports functional programming using:
Concept | C++ Feature |
---|---|
First-Class Functions | Function pointers, lambdas |
Higher-Order Functions | std::function , templates |
Pure Functions | Const correctness, no side effects |
Immutability | const , no global mutations |
Lazy Evaluation | Lambdas and std::function |
Functional Utilities | std::transform , std::accumulate (from <algorithm> and <numeric> ) |
1. Pure Functions in C++
A pure function gives the same output for the same input and causes no side effects.
int square(int x) {
return x * x; // No side effects, pure
}
Compare with:
int result = 0;
int impureSquare(int x) {
result = x * x; // Mutates state, impure
return result;
}
2. Lambda Expressions (Anonymous Functions)
Introduced in C++11, lambdas are concise and powerful.
auto add = [](int a, int b) -> int {
return a + b;
};
cout << add(3, 4); // Outputs 7
Capturing Variables
int factor = 3;
auto multiply = [factor](int x) { return x * factor; };
3. Higher-Order Functions
A higher-order function takes another function as a parameter or returns one.
Example 1: std::function
as a parameter
void applyAndPrint(int x, std::function<int(int)> func) {
cout << func(x) << endl;
}
int doubleIt(int x) { return x * 2; }
applyAndPrint(5, doubleIt); // Outputs 10
Example 2: Function returning a lambda
auto multiplier(int m) {
return [m](int x) { return m * x; };
}
auto times3 = multiplier(3);
cout << times3(5); // Outputs 15
4. Recursion Instead of Loops
FP avoids traditional loops and favors recursion:
int factorial(int n) {
if (n == 0) return 1;
return n * factorial(n - 1);
}
5. Functional STL Utilities
Modern C++ STL includes many functional utilities:
std::transform
vector<int> nums = {1, 2, 3, 4};
vector<int> squares;
transform(nums.begin(), nums.end(), back_inserter(squares), [](int x) {
return x * x;
});
std::accumulate
#include <numeric>
vector<int> nums = {1, 2, 3, 4};
int sum = accumulate(nums.begin(), nums.end(), 0); // Outputs 10
Real-Life Use Cases
- Data Processing Pipelines
- Event-driven Systems
- UI Components (React-style) in GUI frameworks
- Mathematical Computation Engines
- Declarative APIs like filters, mappers, reducers
Functional vs Imperative: Quick Comparison
Feature | Imperative | Functional |
---|---|---|
Focus | How to do it | What to do |
Style | Statements, loops | Expressions, recursion |
Side Effects | Allowed | Avoided |
State | Mutable | Immutable |
Code Size | Usually longer | Often more concise |
Practice Questions
- Write a lambda that checks if a number is prime.
- Create a higher-order function that takes an integer and a function, applies the function to the integer, and prints the result.
- Use
std::transform
to double all elements in a vector. - Create a function that returns a lambda to multiply values by a fixed factor.
- Rewrite a loop-based summation using
std::accumulate
.
Final Thoughts
Even though C++ isn’t a purely functional language, it allows you to write cleaner, safer, and more expressive code using functional paradigms. Knowing how to mix OOP with FP makes you a more flexible, modern C++ developer.