Функциональное программирование в C++: современные возможности и примеры
Функциональное программирование (ФП) — это парадигма, в которой вычисления рассматриваются как вычисление математических функций, избегающих изменения состояния и изменяемых данных. Хотя C++ традиционно ассоциируется с объектно-ориентированным и процедурным программированием, начиная с C++11 и особенно в C++14/17/20, язык получил мощные инструменты для функционального стиля. В этой статье мы разберем ключевые концепции, которые сделают ваш код на C++ более лаконичным, безопасным и выразительным.
1. Лямбда-выражения и замыкания
Лямбда-выражения — это анонимные функции, которые можно использовать на месте. Они стали основой функционального программирования в C++. Лямбда захватывает переменные из окружения (замыкание) и может быть передана в алгоритмы STL.
Синтаксис лямбда-выражения
auto lambda = [захват](параметры) -> возвращаемый_тип { тело };
Пример: сортировка с лямбдой
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> nums = {5, 2, 8, 1, 9};
// Сортировка по убыванию
std::sort(nums.begin(), nums.end(),
[](int a, int b) { return a > b; });
for (int n : nums) std::cout << n << " "; // 9 8 5 2 1
return 0;
}
Замыкания: захват по значению и ссылке
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
int threshold = 5;
std::vector<int> data = {1, 6, 3, 8, 2};
// Захват по значению [=] - копия threshold
auto count_greater = std::count_if(data.begin(), data.end(),
[=](int x) { return x > threshold; });
// Захват по ссылке [&] - можно изменять
auto modify = [&](int &x) {
if (x < threshold) x += threshold;
};
std::for_each(data.begin(), data.end(), modify);
std::cout << "Count: " << count_greater << "\n"; // Count: 2
for (int n : data) std::cout << n << " "; // 6 6 8 8 7
return 0;
}
2. Функторы и std::function
Функторы — это объекты классов, которые перегружают оператор operator(). Они могут хранить состояние и вести себя как функции. std::function — это полиморфная обёртка для хранения любых вызываемых объектов.
Пример функтора
#include <iostream>
class Adder {
private:
int base;
public:
Adder(int b) : base(b) {}
int operator()(int x) const { return base + x; }
};
int main() {
Adder add5(5);
std::cout << add5(10) << "\n"; // 15
std::cout << add5(20) << "\n"; // 25
return 0;
}
std::function: универсальная обёртка
#include <iostream>
#include <functional>
#include <vector>
void process(const std::vector<int>& data, std::function<bool(int)> predicate) {
for (int x : data) {
if (predicate(x)) std::cout << x << " ";
}
std::cout << "\n";
}
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5, 6};
// Можно передавать лямбды
process(numbers, [](int n) { return n % 2 == 0; }); // 2 4 6
process(numbers, [](int n) { return n > 3; }); // 4 5 6
// И функторы
struct GreaterThan {
int limit;
bool operator()(int n) const { return n > limit; }
};
process(numbers, GreaterThan{4}); // 5 6
return 0;
}
3. Алгоритмы STL в функциональном стиле
Библиотека STL предоставляет множество алгоритмов, которые идеально сочетаются с лямбдами и функторами. Вместо циклов используйте std::transform, std::copy_if, std::accumulate и другие.
Map (трансформация) с std::transform
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> input = {1, 2, 3, 4, 5};
std::vector<int> output(input.size());
// Удвоение каждого элемента
std::transform(input.begin(), input.end(), output.begin(),
[](int x) { return x * 2; });
for (int n : output) std::cout << n << " "; // 2 4 6 8 10
return 0;
}
Filter (фильтрация) с std::copy_if
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> data = {1, 2, 3, 4, 5, 6, 7, 8};
std::vector<int> even;
// Оставляем только чётные числа
std::copy_if(data.begin(), data.end(), std::back_inserter(even),
[](int x) { return x % 2 == 0; });
for (int n : even) std::cout << n << " "; // 2 4 6 8
return 0;
}
Reduce (свёртка) с std::accumulate
#include <iostream>
#include <vector>
#include <numeric> // для accumulate
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
// Сумма
int sum = std::accumulate(numbers.begin(), numbers.end(),