std::function e 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 de tipos diferentes de maneira uniforme ou adaptar funções existentes para corresponder a uma assinatura necessária. std::function e std::bind do cabeçalho <functional> resolvem esses problemas.
std::function é um wrapper com apagamento de tipo que pode armazenar qualquer objeto chamável compatível com 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 regular
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 ao fixar alguns argumentos de uma função existente. Use std::placeholders::_1, _2, etc. para marcar os 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"); // Olá, Alice!
auto swapped = std::bind(greet, _2, _1);
swapped("Bob", "Hi"); // Oi, Bob!
}Essas ferramentas são especialmente úteis em POO para armazenar callbacks como membros de classes ou implementar o padrão Strategy. No entanto, o C++ moderno geralmente prefere lambdas a std::bind por oferecerem melhor legibilidade e desempenho — use std::bind principalmente quando precisar reordenar argumentos ou trabalhar com código legado.
Desafio
FácilVamos criar uma calculadora configurável que demonstra o poder de std::function e std::bind. Você criará um sistema no qual as 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 (considere expoentes não negativos).
Calculator.h: defina uma classeCalculatorque usestd::functionpara armazenar e executar operações dinamicamente.Seu Calculator 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 seu cabeçalho MathOperations.- Um membro privado
main.cpp: leia três entradas:- Nome da operação:
add,subtract,multiplyousquare - Primeiro número (inteiro)
- Segundo número (inteiro)
Crie um
Calculatore configure-o com base no nome da operação:- Para
add,subtractemultiply: atribua diretamente a função correspondente à calculadora - Para
square: usestd::bindpara criar uma versão especializada depowerque sempre use expoente 2 e multiplicador 1. A função vinculada deve aceitar dois argumentos, dos quais apenas o primeiro é usado como base (o segundo argumento pode ser ignorado usando um placeholder).
Depois de definir a operação, chame
calculate()com seus dois números e imprima:Result: [value]Em seguida, demonstre a troca de operações atribuindo à calculadora uma lambda que retorna
a + b + 100, 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.
Experimente você mesmo
#include <iostream>
#include <string>
#include <functional>
#include "Calculator.h"
using namespace std;
int main() {
// Ler entradas
string operation;
int num1, num2;
cin >> operation >> num1 >> num2;
// Criar uma instância de Calculator
Calculator calc;
// TODO: Configure a calculadora com base no nome da operação
// Para "add", "subtract", "multiply": atribua a função correspondente diretamente
// Para "square": use std::bind para criar uma versão especializada de power
// que sempre usa expoente 2 e multiplicador 1
// Dica: Use 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: Chame calculate() e imprima o resultado
// Formato: "Result: [value]"
// TODO: Demonstre a troca de operações
// Atribua uma lambda que retorna a + b + 100 ao calculator
// Chame calculate() novamente e imprima 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 POO
Arquivos externosBuild e compilação em C++Arquivos de cabeçalho e arquivos-fonteNamespaces e escopoIntrodução à POO em C++Classes vs. objetosO ponteiro 'this'Métodos (funções membro)Atributos (membros de dados)Noções básicas de construtores e destruidoresRevisão - calculadora simples4Propriedades de Classe
Membros de Instância vs. EstáticosGetters e SettersFunções Membro ConstPalavra-chave MutableMétodos e Variáveis EstáticosFunções e Classes FriendRevisão - Gerenciador de Contas Bancárias7Herança
Herança básicaNíveis de acesso da herançaOrdem de chamada de construtor e destrutorSobrescrita de métodosFunções virtuais e VTableHerança múltiplaHerança virtualRevisão - Hierarquia de funcionários10Visão geral da STL
Visão geral e filosofia da STLContêineres da STLIteradoresAlgoritmos da STLFunctors e expressões lambdaRecapitulação - Frequência de palavras13Padrões de Projeto — Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonFactory e Abstract FactoryPadrão BuilderPadrão ObserverPadrão Strategy2Gerenciamento de Memória
Memória Stack vs. HeapPonteiros e ReferênciasMemória Dinâmica (new/delete)Ponteiros Inteligentes em C++RAII em C++Revisão - Gerenciador de Array Dinâmico5Encapsulamento
Especificadores de acesso em C++Especificadores de acesso em profundidadeOcultação de informaçõesStruct vs ClasseClasses aninhadas e internasRecapitulação - Sistema de registros de alunos8Polimorfismo
Polimorfismo em compilação vs. execuçãoSobrecarga de funçõesFunções virtuais revisadasFunções virtuais purasClasses abstratasDesign de interfaces em C++Conversão dinâmica e RTTIRecapitulação — Calculadora de formas11Conceitos Avançados de OOP
Composição vs. HerançaMixins via CRTPIdioma PimplApagamento de TiposClasses Enum e Tipagem ForteTratamento de Exceções em OOPHierarquias de Exceções Personalizadas14Padrões de Projeto — Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositeRAII como Padrão3Construtores e Destrutores
Construtor PadrãoConstrutor ParametrizadoConstrutor de CópiaConstrutor de MovimentoListas de Inicialização do ConstrutorConstrutores DelegantesAprofundamento em DestrutoresRegra dos Três / Cinco / ZeroRevisão - Classe String6Sobrecarga de Operadores
Introdução à Sobrecarga de OperadoresSobrecarga de Operadores AritméticosSobrecarga de Operadores de ComparaçãoOperadores de FluxoSobrecarga do Operador de AtribuiçãoSobrecarga dos Operadores [] e ()Operadores de Conversão de TipoRecapitulação - Classe Matrix9Templates
Templates de FunçõesTemplates de ClassesEspecialização de TemplatesTemplates VariádicosNoções Básicas de SFINAE e Traits de TiposRecapitulação - Contêiner Genérico12Recursos Modernos do C++
Semântica de Movimentação e RvaluesEncaminhamento PerfeitoExpressões Lambda em Profundidadestd::function e std::bindconstexpr e constevalVinculações Estruturadasoptional, variant, anyPratique por conta própria: Compilador de C++ online