← Назад к вопросам

Что такое функтор (Functor)?

2.3 Middle🔥 171 комментариев
#Язык C++

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Функтор (Functor) в C++

Определение

Функтор (функциональный объект) — это объект класса, у которого перегружен оператор operator(). Благодаря этому объект можно вызывать как функцию.

Функтор — это объект, который ведёт себя как функция. Это мощный способ создать "функцию" со своим состоянием.

Базовый пример

#include <iostream>
using namespace std;

// Простой функтор
class Adder {
public:
    int operator()(int a, int b) const {
        return a + b;
    }
};

int main() {
    Adder add;  // Создаём объект функтора
    cout << add(5, 3) << endl;  // Вызываем как функцию: 8

    // Эквивалентно:
    cout << add.operator()(5, 3) << endl;  // Явный вызов

    return 0;
}

Функтор со состоянием (состояние)

Отличие от обычной функции — функтор может хранить данные:

#include <iostream>
using namespace std;

class Multiplier {
private:
    int factor;

public:
    Multiplier(int f) : factor(f) {}  // Конструктор

    int operator()(int x) const {
        return x * factor;
    }
};

int main() {
    Multiplier multiplyBy3(3);
    Multiplier multiplyBy10(10);

    cout << multiplyBy3(5) << endl;   // 5 * 3 = 15
    cout << multiplyBy10(5) << endl;  // 5 * 10 = 50

    return 0;
}

Каждый объект функтора имеет своё состояние — в отличие от функции!

Использование в STL алгоритмах

Фунторы идеальны для передачи в STL функции, ожидающие callback'ов:

#include <algorithm>
#include <vector>
#include <iostream>
using namespace std;

// Функтор-компаратор для сортировки
class Compare {
private:
    bool ascending;

public:
    Compare(bool asc = true) : ascending(asc) {}

    bool operator()(int a, int b) const {
        return ascending ? a < b : a > b;  // По возрастанию или убыванию
    }
};

int main() {
    vector<int> nums = {3, 1, 4, 1, 5, 9, 2, 6};

    // Сортировка по возрастанию
    sort(nums.begin(), nums.end(), Compare(true));
    for (int n : nums) cout << n << " ";
    cout << endl;  // 1 1 2 3 4 5 6 9

    // Сортировка по убыванию
    sort(nums.begin(), nums.end(), Compare(false));
    for (int n : nums) cout << n << " ";
    cout << endl;  // 9 6 5 4 3 2 1 1

    return 0;
}

Функтор с mutable состоянием

Фунтор может изменять своё состояние:

#include <iostream>
using namespace std;

class Counter {
private:
    int count;

public:
    Counter() : count(0) {}

    int operator()(int x) {
        count += x;
        return count;  // Возвращаем累積сумму
    }

    int getCount() const { return count; }
};

int main() {
    Counter counter;

    cout << counter(5) << endl;   // 5
    cout << counter(3) << endl;   // 8
    cout << counter(2) << endl;   // 10
    cout << counter.getCount() << endl;  // 10

    return 0;
}

Функтор vs Лямбда

С введением лямбд в C++11, функторы используются реже, но они разные инструменты:

#include <algorithm>
#include <vector>
using namespace std;

// Способ 1: Функтор (до C++11)
class IsEven {
public:
    bool operator()(int x) const { return x % 2 == 0; }
};

int main() {
    vector<int> nums = {1, 2, 3, 4, 5, 6};

    // Способ 1: Функтор
    auto even1 = find_if(nums.begin(), nums.end(), IsEven());

    // Способ 2: Лямбда (C++11, предпочтительнее)
    auto even2 = find_if(nums.begin(), nums.end(),
                        [](int x) { return x % 2 == 0; });

    // Способ 3: Обычная функция
    auto even3 = find_if(nums.begin(), nums.end(),
                        [](int x) { return x % 2 == 0; });

    return 0;
}

Сравнение:

ХарактеристикаФункторЛямбдаФункция
СостояниеМожет иметьМожет захватитьНет
СинтаксисМногословныйКомпактныйПростой
ПроизводительностьХорошаяОтличная (инлайн)Хорошая
ПереиспользованиеХорошееПлохоеХорошее
Когда использоватьСложная логикаПростые операцииПубличный API

std::function и функторы

std::function позволяет хранить любой callable объект (функцию, функтор, лямбду):

#include <functional>
#include <iostream>
using namespace std;

class Printer {
private:
    string prefix;

public:
    Printer(const string& p) : prefix(p) {}

    void operator()(const string& msg) const {
        cout << prefix << ": " << msg << endl;
    }
};

int main() {
    function<void(const string&)> callback;

    // Функтор
    callback = Printer("INFO");
    callback("Application started");
    // Output: INFO: Application started

    // Лямбда
    callback = [](const string& msg) { cout << "LOG: " << msg << endl; };
    callback("Processing data");
    // Output: LOG: Processing data

    return 0;
}

Функторы для трансформаций (transform)

#include <algorithm>
#include <vector>
#include <iostream>
using namespace std;

class Square {
public:
    int operator()(int x) const { return x * x; }
};

int main() {
    vector<int> nums = {1, 2, 3, 4, 5};
    vector<int> squared(nums.size());

    // Применяем функтор к каждому элементу
    transform(nums.begin(), nums.end(), squared.begin(), Square());

    for (int n : squared)
        cout << n << " ";
    cout << endl;  // 1 4 9 16 25

    return 0;
}

Специализированные функторы STL

Стандартная библиотека содержит встроенные функторы:

#include <functional>
#include <algorithm>
#include <vector>
using namespace std;

int main() {
    vector<int> nums = {5, 2, 8, 1, 9};

    // less<int> — функтор сравнения
    sort(nums.begin(), nums.end(), less<int>());      // По возрастанию
    sort(nums.begin(), nums.end(), greater<int>());   // По убыванию

    // plus<int>, minus<int>, multiplies<int> и т.д.
    int a = 10, b = 3;
    cout << plus<int>()(a, b) << endl;        // 13
    cout << minus<int>()(a, b) << endl;       // 7
    cout << multiplies<int>()(a, b) << endl;  // 30

    return 0;
}

Функторы vs Указатели на функции

#include <iostream>
using namespace std;

// Обычная функция
int addFunction(int a, int b) {
    return a + b;
}

// Функтор
class AddFunctor {
public:
    int operator()(int a, int b) const {
        return a + b;
    }
};

int main() {
    // Указатель на функцию
    int (*funcPtr)(int, int) = addFunction;
    cout << funcPtr(5, 3) << endl;  // 8

    // Функтор
    AddFunctor addObj;
    cout << addObj(5, 3) << endl;   // 8

    // Функтор может быть инлайнирован компилятором,
    // указатель на функцию — нет (дополнительный оверхед)!

    return 0;
}

Когда использовать функторы

Используйте функторы:

  1. Для состояния — если нужно хранить данные между вызовами
  2. Для переиспользования — один функтор, разные параметры через конструктор
  3. Для производительности — компилятор может инлайнить код
  4. Для сложной логики — когда лямбда слишком велика

Переходите на лямбды (C++11+):

  • Для простых операций
  • Для одноразовых функций
  • Когда нет нужды в состоянии

Современная C++ практика

C++11+ изменил парадигму:

// Старый способ (до C++11): функторы везде
class Comparator { ... };
sort(v.begin(), v.end(), Comparator());

// Новый способ (C++11+): лямбды предпочтительнее
sort(v.begin(), v.end(), [](int a, int b) { return a < b; });

// Но функторы остаются полезны для сложной логики
class ComplexComparator {
private:
    // состояние, параметры и т.д.
};

Итоговые рекомендации

Функтор — это объект класса с перегруженным operator(). Он:

  1. Может хранить состояние (данные)
  2. Переиспользуется с разными параметрами конструктора
  3. Вызывается как функция
  4. Встроен в STL алгоритмы
  5. Остаётся мощным инструментом даже в эпоху лямбд

Хотя лямбды (C++11+) вытеснили функторы из повседневного использования, понимание функторов критично для эффективного C++ кода и работы с STL.