Menu
Coddy logo textTech

std::function и std::bind

Часть раздела Объектно-ориентированное программирование путешествия по C++ на Coddy. Урок 86 из 104.

Хотя лямбда-выражения обладают широкими возможностями, иногда необходимо хранить вызываемые объекты разных типов единообразным способом или адаптировать существующие функции для соответствия требуемой сигнатуре. std::function и std::bind из заголовочного файла <functional> решают эти задачи.

std::function — это оболочка со стёртым типом, которая может содержать любой вызываемый объект, соответствующий определённой сигнатуре, — функции, лямбды или функциональные объекты:

#include <iostream>
#include <functional>

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

int main() {
    std::function<int(int, int)> operation;
    
    operation = add;                              // Обычная функция
    std::cout << operation(3, 4) << "\n";        // 7
    
    operation = [](int a, int b) { return a * b; }; // Лямбда
    std::cout << operation(3, 4) << "\n";        // 12
}

std::bind создаёт новый вызываемый объект, фиксируя некоторые arguments существующей function. Используйте std::placeholders::_1, _2 и т. д., чтобы обозначить arguments, которые остаются переменными:

#include <iostream>
#include <functional>

void greet(const std::string& greeting, const std::string& name) {
    std::cout << greeting << ", " << name << "!\n";
}

int main() {
    using namespace std::placeholders;
    
    auto sayHello = std::bind(greet, "Hello", _1);
    sayHello("Alice");  // Привет, Алиса!
    
    auto swapped = std::bind(greet, _2, _1);
    swapped("Bob", "Hi");  // Привет, Боб!
}

Эти инструменты особенно полезны в ООП для хранения обратных вызовов в качестве членов class или реализации паттерна Strategy. Однако в современном C++ часто предпочитают лямбды вместо std::bind благодаря лучшей читаемости и производительности — используйте std::bind главным образом, когда нужно изменить порядок аргументов или работать с устаревшим кодом.

challenge icon

Задание

Легко

Давайте создадим настраиваемый калькулятор, демонстрирующий возможности std::function и std::bind. Вы создадите систему, в которой математические операции можно хранить, заменять и настраивать во время выполнения, показывая, как эти инструменты обеспечивают гибкое управление callback-функциями.

Вы организуете код в трёх файлах:

  • MathOperations.h: Declare набор отдельных математических функций, которые будут служить библиотекой операций.

    Create эти функции:

    • add(int a, int b): возвращает сумму
    • subtract(int a, int b): возвращает difference (a - b)
    • multiply(int a, int b): возвращает произведение
    • power(int base, int exponent, int multiplier): возвращает multiplier * (base ^ exponent). Используйте простой цикл, чтобы calculate степень (assume неотрицательные exponents).
  • Calculator.h: Declare Calculator class, который использует std::function для динамического хранения и выполнения операций.

    Ваш Calculator должен содержать:

    • Приватный член std::function<int(int, int)> для хранения current бинарной операции
    • setOperation(std::function<int(int, int)> op): устанавливает current операцию
    • calculate(int a, int b): Executes сохранённую операцию и возвращает результат

    Include необходимые заголовочные файлы (<functional>) и убедитесь, что вы подключили заголовочный файл MathOperations.

  • main.cpp: Считайте три inputs:
    1. Название операции: add, subtract, multiply или square
    2. Первое число (целое)
    3. Второе число (целое)

    Create Calculator и Configure его на основе названия операции:

    • Для add, subtract и multiply: assign соответствующую function непосредственно калькулятору
    • Для square: используйте std::bind, чтобы создать специализированную версию power, которая always использует exponent 2 и multiplier 1. Связанная function должна принимать два аргумента, при этом только первый используется как base (второй аргумент можно пропустить с помощью заполнителя).

    После установки операции вызовите calculate() с двумя вашими числами и выведите:

    Result: [value]

    Затем продемонстрируйте замену операций, assign лямбда-функцию, которая возвращает a + b + 100, калькулятору, выполните её с теми же inputs и выведите:

    With bonus: [value]

Например, с inputs add, 10 и 5:

Result: 15
With bonus: 115

С inputs multiply, 7 и 3:

Result: 21
With bonus: 110

С inputs square, 4 и 0:

Result: 16
With bonus: 104

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

Попробуйте сами

#include <iostream>
#include <string>
#include <functional>
#include "Calculator.h"

using namespace std;

int main() {
    // Прочитать входные данные
    string operation;
    int num1, num2;
    cin >> operation >> num1 >> num2;
    
    // Создать экземпляр Calculator
    Calculator calc;
    
    // TODO: Настроить калькулятор на основе имени операции
    // Для "add", "subtract", "multiply": назначить соответствующую функцию напрямую
    // Для "square": использовать std::bind для создания специализированной версии power
    //   которая всегда использует показатель степени 2 и множитель 1
    //   Подсказка: Используйте std::placeholders::_1 для аргумента основания
    
    if (operation == "add") {
        // Ваш код здесь
    } else if (operation == "subtract") {
        // Ваш код здесь
    } else if (operation == "multiply") {
        // Ваш код здесь
    } else if (operation == "square") {
        // Ваш код здесь - используйте std::bind с функцией power
    }
    
    // TODO: Вызвать calculate() и вывести результат
    // Формат: "Result: [value]"
    
    // TODO: Продемонстрировать замену операций
    // Присвойте калькулятору лямбду, которая возвращает a + b + 100
    // Снова вызовите calculate() и выведите результат
    // Формат: "With bonus: [value]"
    
    return 0;
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор C++