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

Что такое std::function и std::bind?

1.0 Junior🔥 131 комментариев
#STL контейнеры и алгоритмы#ООП и проектирование#Язык C++

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

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

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

std::function и std::bind

Определение

std::function — это шаблонный класс-обёртка, который может хранить любой callable объект (функцию, функтор, лямбду, связанную функцию) с определённой сигнатурой. Это позволяет работать с функциями как с данными.

std::bind — это функция, которая связывает (bind) параметры функции с конкретными значениями или плейсхолдерами, создавая новую функцию с изменённой сигнатурой.

std::function

Синтаксис

#include <functional>
using namespace std;

std::function<ReturnType(Param1, Param2, ...)> func;

Примеры типов:

std::function<int(int, int)>        // Функция: (int, int) → int
std::function<void(string)>         // Функция: (string) → void
std::function<bool(const int&)>     // Функция: (const int&) → bool

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

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

void printMessage(const string& msg) {
    cout << msg << endl;
}

int main() {
    // std::function может хранить обычную функцию
    function<void(const string&)> callback = printMessage;
    callback("Hello from function!");

    // Или лямбду
    callback = [](const string& msg) {
        cout << "Lambda: " << msg << endl;
    };
    callback("Hello from lambda!");

    // Или функтор
    struct Printer {
        void operator()(const string& msg) const {
            cout << "Functor: " << msg << endl;
        }
    };
    callback = Printer();
    callback("Hello from functor!");

    return 0;
}

Вывод:

Hello from function!
Lambda: Hello from lambda!
Functor: Hello from functor!

Хранение в контейнерах

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

int main() {
    // Вектор функций разных типов!
    vector<function<int(int)>> handlers;

    // Добавляем лямбду
    handlers.push_back([](int x) { return x * 2; });

    // Добавляем обычную функцию
    handlers.push_back([](int x) { return x + 10; });

    // Добавляем функтор
    handlers.push_back([](int x) { return x * x; });

    // Вызываем все обработчики
    for (int i = 0; i < handlers.size(); ++i) {
        cout << "Handler " << i << ": " << handlers[i](5) << endl;
    }

    return 0;
}

Вывод:

Handler 0: 10
Handler 1: 15
Handler 2: 25

Проверка пустоты

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

int main() {
    function<void()> func;

    // Проверка: функция не установлена
    if (!func) {
        cout << "Function is empty" << endl;
    }

    func = []() { cout << "Called" << endl; };

    // Теперь можно вызывать
    if (func) {
        func();
    }

    return 0;
}

std::bind

Синтаксис

#include <functional>
using namespace std;

auto boundFunc = bind(function, arg1, arg2, ...);

Плейсхолдеры:

using namespace placeholders;
_1, _2, _3, ...  // Аргументы связанной функции

Пример 1: Связывание всех аргументов

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

int add(int a, int b) {
    return a + b;
}

int main() {
    // Связываем обе переменные
    auto add5_and_10 = bind(add, 5, 10);
    cout << add5_and_10() << endl;  // 15

    return 0;
}

Пример 2: Связывание с плейсхолдерами

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

int multiply(int a, int b) {
    return a * b;
}

int main() {
    // Связываем первый аргумент, второй остаётся плейсхолдером
    auto multiplyBy10 = bind(multiply, 10, _1);
    cout << multiplyBy10(5) << endl;  // 10 * 5 = 50
    cout << multiplyBy10(3) << endl;  // 10 * 3 = 30

    // Меняем порядок аргументов
    auto reverseMultiply = bind(multiply, _2, _1);
    cout << reverseMultiply(5, 10) << endl;  // 10 * 5 = 50

    return 0;
}

Пример 3: Связывание метода класса

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

class Calculator {
private:
    int offset = 0;

public:
    Calculator(int off) : offset(off) {}

    int add(int a, int b) const {
        return a + b + offset;
    }
};

int main() {
    Calculator calc(100);

    // Связываем метод класса с объектом
    auto boundMethod = bind(&Calculator::add, &calc, _1, _2);
    cout << boundMethod(5, 3) << endl;  // 5 + 3 + 100 = 108

    return 0;
}

Пример 4: Комбинирование bind и function

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

class EventHandler {
public:
    void onEvent(int eventId, const string& message) {
        cout << "Event " << eventId << ": " << message << endl;
    }
};

int main() {
    EventHandler handler;
    vector<function<void(int, const string&)>> callbacks;

    // Связываем метод и добавляем в вектор
    callbacks.push_back(bind(&EventHandler::onEvent, &handler, _1, _2));

    // Вызываем через function
    callbacks[0](1, "Button clicked");
    callbacks[0](2, "File loaded");

    return 0;
}

Практические примеры

Пример 1: Обработчик событий

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

class EventSystem {
private:
    map<string, function<void(int)>> handlers;

public:
    void subscribe(const string& event, function<void(int)> callback) {
        handlers[event] = callback;
    }

    void emit(const string& event, int data) {
        if (handlers.count(event))
            handlers[event](data);
    }
};

int main() {
    EventSystem events;

    // Подписываемся на событие с лямбдой
    events.subscribe("click", [](int x) {
        cout << "Clicked at " << x << endl;
    });

    // Генерируем событие
    events.emit("click", 42);

    return 0;
}

Пример 2: Task scheduler с bind

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

class Logger {
public:
    void log(const string& level, const string& message) {
        cout << "[" << level << "] " << message << endl;
    }
};

int main() {
    Logger logger;
    vector<function<void()>> tasks;

    // Создаём задачи с предзаполненными аргументами
    tasks.push_back(bind(&Logger::log, &logger, "INFO", "Task 1"));
    tasks.push_back(bind(&Logger::log, &logger, "ERROR", "Task 2"));
    tasks.push_back(bind(&Logger::log, &logger, "WARN", "Task 3"));

    // Выполняем все задачи
    for (auto& task : tasks)
        task();

    return 0;
}

Вывод:

[INFO] Task 1
[ERROR] Task 2
[WARN] Task 3

Пример 3: Параметризованная сортировка

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

class Comparator {
private:
    int threshold;

public:
    Comparator(int t) : threshold(t) {}

    bool compare(int a, int b) const {
        // Сортируем относительно threshold
        return abs(a - threshold) < abs(b - threshold);
    }
};

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

    // Используем bind для параметризованного компаратора
    sort(nums.begin(), nums.end(),
         bind(&Comparator::compare, &comp, _1, _2));

    for (int n : nums)
        cout << n << " ";
    cout << endl;  // 5 3 8 1 2 (близкие к 5 в начале)

    return 0;
}

std::bind vs Лямбды (C++11+)

Лямбды практически вытеснили std::bind, но оба инструмента имеют применение:

using namespace placeholders;
using namespace std;

// Способ 1: std::bind (старый, C++11)
auto boundFunc = bind(multiply, 10, _1);

// Способ 2: Лямбда (современный, предпочтительный)
auto lambdaFunc = [](int x) { return multiply(10, x); };

// Способ 3: std::bind со сложной логикой
auto complex = bind([](int a, int b) { return a * b + 10; }, _1, 5);

Сравнение:

Характеристикаstd::bindЛямбда
СинтаксисМногословныйКомпактный
ЧитаемостьСложнаяПростая
ПроизводительностьХорошаяОтличная (инлайн)
ГибкостьВысокаяВысокая
Когда использоватьСложные сценарииБольшинство случаев

Проблема: std::bind медленнее

// std::bind может быть медленнее из-за оверхеда
using namespace placeholders;
auto bound = bind(expensiveFunc, _1, _2);
bound(10, 20);  // Есть оверхед на вызов

// Лямбда часто инлайнится
auto lambda = [](int a, int b) { return expensiveFunc(a, b); };
lambda(10, 20);  // Может быть полностью инлайнирована

Реальные применения std::function

Пример 1: Dependency Injection

#include <functional>
using namespace std;

class Service {
private:
    function<string()> dataProvider;  // Инъекция зависимости

public:
    Service(function<string()> provider) : dataProvider(provider) {}

    void process() {
        cout << "Data: " << dataProvider() << endl;
    }
};

int main() {
    // Можно инъектировать разные реализации
    Service s1([]() { return "From Database"; });
    Service s2([]() { return "From File"; });

    s1.process();
    s2.process();

    return 0;
}

Пример 2: Strategy Pattern

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

class Processor {
private:
    function<int(int)> strategy;  // Стратегия обработки

public:
    void setStrategy(function<int(int)> s) { strategy = s; }

    int process(int input) {
        return strategy ? strategy(input) : input;
    }
};

int main() {
    Processor p;

    p.setStrategy([](int x) { return x * 2; });
    cout << p.process(5) << endl;  // 10

    p.setStrategy([](int x) { return x + 100; });
    cout << p.process(5) << endl;  // 105

    return 0;
}

Best practices

Используйте std::function для:

  1. Callback'ов и event handlers
  2. Strategy pattern
  3. Dependency injection
  4. Хранения функций разных типов

Используйте std::bind редко:

  1. Только для сложных сценариев связывания
  2. Предпочитайте лямбды (понятнее и быстрее)
  3. Хорошо для адаптации legacy-кода

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

std::function — это универсальная обёртка для callable объектов, критична для:

  1. Event-driven архитектур (callbacks)
  2. Dependency injection
  3. Strategy pattern и других паттернов проектирования
  4. Полиморфизма функций

std::bind — это инструмент для связывания параметров, но:

  1. Вытеснена лямбдами (C++11+)
  2. Медленнее из-за оверхеда
  3. Сложнее читать
  4. Используйте только если нет альтернативы

Современный подход (C++11+):

// Вместо bind — используйте лямбду
// Вместо function<> для простых случаев — используйте template параметры
template<typename Func>
void execute(Func callback) {  // template параметр быстрее!
    callback();
}

// std::function для полиморфизма
std::function<void()> polymorphic = ...;

Мастерство в использовании этих инструментов необходимо для написания гибкого и эффективного C++ кода!