Что такое функциональное программирование (ФП) и как его принципы применяются в C++?

«Что такое функциональное программирование (ФП) и как его принципы применяются в C++?» — вопрос из категории Архитектура, который задают на 25% собеседований C/C++ Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Функциональное программирование (ФП) — это парадигма, рассматривающая программу как вычисление математических функций, избегающая изменяемого состояния и побочных эффектов. C++ — мультипарадигменный язык, который поддерживает многие принципы ФП.

Ключевые принципы ФП и их реализация в C++:

  1. Чистые функции (Pure Functions): Функция, результат которой зависит только от входных аргументов и не имеет побочных эффектов (не изменяет глобальное состояние).

    // Чистая функция
    int square(int x) { return x * x; }
    // НЕ чистая функция (зависит от глобальной переменной, имеет побочный эффект)
    int global = 0;
    int impure(int x) { global += x; return x * 2; }
  2. Функции как объекты первого класса: С ними можно работать как с данными: передавать как аргументы, возвращать из функций, хранить в переменных.

    #include <functional>
    #include <vector>
    #include <algorithm>
    
    // Принимаем функцию как аргумент (функция высшего порядка)
    void apply_to_all(std::vector<int>& vec, std::function<int(int)> func) {
        for (int& elem : vec) {
            elem = func(elem);
        }
    }
    
    int main() {
        std::vector<int> data = {1, 2, 3};
        apply_to_all(data, square); // Передаем указатель на чистую функцию
        apply_to_all(data, [](int x){ return x + 1; }); // Передаем лямбду
    }
  3. Иммутабельность (Неизменяемость): Предпочтение константных данных. В C++ этому способствуют const, constexpr.

    const std::vector<int> immutable_data = {1, 2, 3};
    // immutable_data.push_back(4); // Ошибка компиляции
    
    // Алгоритмы, не изменяющие исходные данные, а создающие новые
    std::vector<int> transformed;
    std::transform(immutable_data.begin(), immutable_data.end(),
                   std::back_inserter(transformed),
                   [](int x){ return x * 2; });
  4. Рекурсия: Хотя C++ не гарантирует TCO (оптимизацию хвостовой рекурсии), рекурсия является основным инструментом ФП для организации циклов.

Польза в C++ проектах: Применение ФП-принципов (особенно чистых функций и иммутабельности) упрощает тестирование, рассуждение о потоке данных и параллелизацию, так как код меньше зависит от разделяемого состояния.