Menu
Coddy logo textTech

std::function & std::bind

Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy — lição 86 de 104.

Embora as lambdas sejam poderosas, às vezes você precisa armazenar objetos chamáveis com tipos diferentes de uma forma uniforme, ou adaptar funções existentes para corresponderem a uma assinatura exigida. std::function e std::bind do cabeçalho <functional> resolvem esses problemas.

std::function é um wrapper com apagamento de tipo (type-erased) que pode conter qualquer chamável que corresponda a uma assinatura específica — funções, lambdas ou objetos de função:

#include <iostream>
#include <functional>

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

int main() {
    std::function<int(int, int)> operation;
    
    operation = add;                              // Função comum
    std::cout << operation(3, 4) << "\n";        // 7
    
    operation = [](int a, int b) { return a * b; }; // Lambda
    std::cout << operation(3, 4) << "\n";        // 12
}

std::bind cria um novo objeto chamável fixando alguns argumentos de uma função existente. Use std::placeholders::_1, _2, etc., para marcar argumentos que permanecem variáveis:

#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");  // Hello, Alice!
    
    auto swapped = std::bind(greet, _2, _1);
    swapped("Bob", "Hi");  // Hi, Bob!
}

Essas ferramentas são especialmente úteis em OOP para armazenar callbacks como membros de classe ou implementar o padrão Strategy. No entanto, o C++ moderno frequentemente prefere lambdas em vez de std::bind para melhor legibilidade e desempenho — use std::bind principalmente quando precisar reordenar argumentos ou trabalhar com código legado.

challenge icon

Desafio

Fácil

Vamos construir uma calculadora configurável que demonstra o poder de std::function e std::bind. Você criará um sistema onde operações matemáticas podem ser armazenadas, trocadas e personalizadas em tempo de execução — mostrando como essas ferramentas permitem um gerenciamento flexível de callbacks.

Você organizará seu código em três arquivos:

  • MathOperations.h: Defina uma coleção de funções matemáticas independentes que servirão como sua biblioteca de operações.

    Crie estas funções:

    • add(int a, int b) — retorna a soma
    • subtract(int a, int b) — retorna a diferença (a - b)
    • multiply(int a, int b) — retorna o produto
    • power(int base, int exponent, int multiplier) — retorna multiplier * (base ^ exponent). Use um loop simples para calcular a potência (assuma expoentes não negativos).
  • Calculator.h: Defina uma classe Calculator que usa std::function para armazenar e executar operações dinamicamente.

    Sua Calculadora deve ter:

    • Um membro privado std::function<int(int, int)> para armazenar a operação binária atual
    • setOperation(std::function<int(int, int)> op) — define a operação atual
    • calculate(int a, int b) — executa a operação armazenada e retorna o resultado

    Inclua os cabeçalhos necessários (<functional>) e certifique-se de incluir o seu cabeçalho MathOperations.

  • main.cpp: Leia três entradas:
    1. Nome da operação: add, subtract, multiply, ou square
    2. Primeiro número (inteiro)
    3. Segundo número (inteiro)

    Crie uma Calculator e configure-a com base no nome da operação:

    • Para add, subtract, e multiply: atribua a função correspondente diretamente à calculadora
    • Para square: use std::bind para criar uma versão especializada de power que sempre usa o expoente 2 e o multiplicador 1. A função vinculada deve aceitar dois argumentos onde apenas o primeiro é usado como base (o segundo argumento pode ser ignorado usando um placeholder).

    Após definir a operação, chame calculate() com seus dois números e imprima:

    Result: [value]

    Em seguida, demonstre a troca de operações atribuindo uma lambda que retorna a + b + 100 à calculadora, executando-a com as mesmas entradas e imprimindo:

    With bonus: [value]

Por exemplo, com as entradas add, 10, e 5:

Result: 15
With bonus: 115

Com as entradas multiply, 7, e 3:

Result: 21
With bonus: 110

Com as entradas square, 4, e 0:

Result: 16
With bonus: 104

Este desafio mostra como std::function fornece uma maneira uniforme de armazenar diferentes tipos chamáveis (funções regulares, funções vinculadas e lambdas), enquanto std::bind permite adaptar funções existentes fixando alguns de seus argumentos.

Folha de consulta

O cabeçalho <functional> fornece std::function e std::bind para trabalhar com objetos chamáveis (callable objects) de forma flexível.

std::function é um wrapper com apagamento de tipo (type-erased) que pode armazenar qualquer chamável que corresponda a uma assinatura específica:

#include <functional>

std::function<int(int, int)> operation;

operation = add;                              // Função regular
operation = [](int a, int b) { return a * b; }; // Lambda

std::bind cria um novo chamável fixando alguns argumentos de uma função existente. Use std::placeholders::_1, _2, etc. para argumentos variáveis:

#include <functional>

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

using namespace std::placeholders;

auto sayHello = std::bind(greet, "Hello", _1);  // Fixa o primeiro argumento
sayHello("Alice");  // Hello, Alice!

auto swapped = std::bind(greet, _2, _1);  // Reordena os argumentos
swapped("Bob", "Hi");  // Hi, Bob!

Essas ferramentas são úteis para armazenar callbacks como membros de classe ou implementar sistemas de callback flexíveis. O C++ moderno geralmente prefere lambdas em vez de std::bind para uma melhor legibilidade.

Experimente você mesmo

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

using namespace std;

int main() {
    // Ler as entradas
    string operation;
    int num1, num2;
    cin >> operation >> num1 >> num2;
    
    // Criar uma instância de Calculator
    Calculator calc;
    
    // TODO: Configurar a calculadora com base no nome da operação
    // Para "add", "subtract", "multiply": atribuir a função correspondente diretamente
    // Para "square": usar std::bind para criar uma versão especializada de power
    //   que sempre usa expoente 2 e multiplicador 1
    //   Hint: Usar std::placeholders::_1 para o argumento base
    
    if (operation == "add") {
        // Seu código aqui
    } else if (operation == "subtract") {
        // Seu código aqui
    } else if (operation == "multiply") {
        // Seu código aqui
    } else if (operation == "square") {
        // Seu código aqui - use std::bind com a função power
    }
    
    // TODO: Chamar calculate() e imprimir o resultado
    // Formato: "Result: [value]"
    
    // TODO: Demonstrar a troca de operações
    // Atribuir uma lambda que retorna a + b + 100 para a calculadora
    // Chamar calculate() novamente e imprimir o resultado
    // Formato: "With bonus: [value]"
    
    return 0;
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos