Functional Programming in C++ — A Beginner-Friendly Guide
Learn how to write cleaner, safer, and more expressive C++ code using functional programming techniques: pure functions, lambdas, higher-order functions, recursion, and STL utilities.
- What functional programming is and its core principles
- How to write pure functions with no side effects
- How to use lambda expressions (anonymous functions)
- How to write higher-order functions in C++
- How to use functional STL utilities like transform and accumulate
1What is functional programming?
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.
- 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"
2Key functional features in C++
C++ supports functional programming using these features:
| 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>) |
3Pure 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 an impure function:
int result = 0; int impureSquare(int x) { result = x * x; // Mutates global state, impure return result; }
4Lambda expressions (anonymous functions)
Introduced in C++11, lambdas are concise and powerful anonymous functions.
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; };
[=]– capture all by value[&]– capture all by reference[x, &y]– capture x by value, y by reference
5Higher-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
6Recursion instead of loops
Functional programming avoids traditional loops and favors recursion.
int factorial(int n) { if (n == 0) return 1; return n * factorial(n - 1); }
7Functional STL utilities
Modern C++ STL includes many functional utilities that make functional programming practical.
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
std::find_if– find element matching a predicatestd::count_if– count elements matching a predicatestd::remove_if– remove elements matching a predicatestd::all_of,std::any_of,std::none_of– check properties of ranges
8Real-life use cases
- Data Processing Pipelines – transforming and filtering data streams
- Event-driven Systems – callbacks and event handlers
- UI Components – React-style declarative UI in GUI frameworks
- Mathematical Computation Engines – pure functions for numerical algorithms
- Declarative APIs – filters, mappers, reducers similar to JavaScript's Array methods
9Functional 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 |
10Practice 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::transformto 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.
Bonus: Combine std::transform and std::accumulate to compute the sum of squares of a vector.
11Quiz yourself
Final thoughts
Even though C++ is not 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.
