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.
Desafio
FácilVamos 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 somasubtract(int a, int b)— retorna a diferença (a - b)multiply(int a, int b)— retorna o produtopower(int base, int exponent, int multiplier)— retornamultiplier * (base ^ exponent). Use um loop simples para calcular a potência (assuma expoentes não negativos).
Calculator.h: Defina uma classeCalculatorque usastd::functionpara 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 atualcalculate(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.- Um membro privado
main.cpp: Leia três entradas:- Nome da operação:
add,subtract,multiply, ousquare - Primeiro número (inteiro)
- Segundo número (inteiro)
Crie uma
Calculatore configure-a com base no nome da operação:- Para
add,subtract, emultiply: atribua a função correspondente diretamente à calculadora - Para
square: usestd::bindpara criar uma versão especializada depowerque 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]- Nome da operação:
Por exemplo, com as entradas add, 10, e 5:
Result: 15
With bonus: 115Com as entradas multiply, 7, e 3:
Result: 21
With bonus: 110Com as entradas square, 4, e 0:
Result: 16
With bonus: 104Este 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; }; // Lambdastd::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;
}
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
1Fundamentos de OOP
Arquivos ExternosBuild e Compilação em C++Arquivos Header e SourceNamespaces e EscopoIntrodução a OOP em C++Classes vs ObjetosO Ponteiro 'this'Métodos (Funções Membro)Atributos (Membros de Dados)Básico de Ctors e DtorsRecapitulação - Calculadora Simples4Propriedades de Classe
Membros de Instância vs EstáticosGetters e SettersFunções de Membro ConstPalavra-chave MutableMétodos e Variáveis EstáticosFunções e Classes FriendRecapitulação - Gerenciador de Conta Bancária7Herança
Herança BásicaNíveis de Acesso na HerançaOrdem de Chamada de Ctor e DtorSobrescrita de MétodosFunções Virtuais e VTableHerança MúltiplaHerança VirtualRecapitulação - Hierarquia de Funcionários2Gerenciamento de Memória
Memória Stack vs HeapPonteiros e ReferênciasMemória Dinâmica (new/delete)Smart Pointers em C++RAII em C++Recapitulação - Gerenciador de Array Dinâmico5Encapsulamento
Especificadores de Acesso em C++Especificadores de Acesso em DetalhesOcultação de InformaçãoStruct vs ClassClasses Aninhadas e InternasRecapitulação - Sistema de Registros de Alunos8Polimorfismo
Polimorfismo de Compilação vs Tempo de ExecuçãoSobrecarga de FunçõesFunções Virtuais RevisitadasFunções Virtuais PurasClasses AbstratasDesign de Interfaces em C++Dynamic Casting e RTTIRecapitulação - Calculadora de Formas3Construtores e Destrutores
Construtor PadrãoConstrutor ParametrizadoConstrutor de CópiaConstrutor de MovimentaçãoListas de Inicialização do ConstrutorConstrutores DelegadosMergulho Profundo em DestrutoresRegra dos Três / Cinco / ZeroRecapitulação - Classe String6Sobrecarga de Operadores
Introdução à Sobrecarga de OperadoresSobrecarga de Operadores AritméticosSobrecarga de Operadores de ComparaçãoOperadores de StreamSobrecarga do Operador de AtribuiçãoSobrecarga dos Operadores [] e ()Operadores de Conversão de TipoRecapitulação - Classe Matrix9Templates
Templates de FunçãoTemplates de ClasseEspecialização de TemplatesTemplates VariádicosFundamentos de SFINAE e Type TraitsRecapitulação - Container Genérico12Recursos do C++ Moderno
Semântica de Movimento e RvaluesPerfect ForwardingExpressões Lambda em Detalhesstd::function & std::bindconstexpr e constevalStructured Bindingsoptional, variant, any