std::function e std::bind
Fa parte della sezione Programmazione orientata agli oggetti del percorso C++ di Coddy. Lezione 86 di 104.
Sebbene le lambda siano potenti, a volte è necessario archiviare oggetti chiamabili di tipi diversi in modo uniforme, oppure adattare funzioni esistenti a una firma richiesta. std::function e std::bind dell’header <functional> risolvono questi problemi.
std::function è un wrapper con tipo cancellato che può contenere qualsiasi oggetto chiamabile corrispondente a una firma specifica: funzioni, lambda o oggetti funzione.
#include <iostream>
#include <functional>
int add(int a, int b) { return a + b; }
int main() {
std::function<int(int, int)> operation;
operation = add; // Funzione normale
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 crea un nuovo oggetto chiamabile fissando alcuni argomenti di una funzione esistente. Usa std::placeholders::_1, _2 e così via per contrassegnare gli argomenti che rimangono variabili:
#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!
}Questi strumenti sono particolarmente utili nella programmazione OOP per archiviare callback come membri di una classe o implementare il pattern Strategy. Tuttavia, il C++ moderno spesso preferisce le lambda a std::bind per una maggiore leggibilità e prestazioni: usa std::bind soprattutto quando devi riordinare gli argomenti o lavorare con codice legacy.
Sfida
FacileCostruiamo una calcolatrice configurabile che dimostra la potenza di std::function e std::bind. Creerai un sistema in cui le operazioni matematiche possono essere memorizzate, sostituite e personalizzate durante l’esecuzione: vedrai come questi strumenti consentono una gestione flessibile delle callback.
Organizzerai il codice in tre file:
MathOperations.h: definisci una raccolta di funzioni matematiche indipendenti che fungeranno da libreria di operazioni.Crea queste funzioni:
add(int a, int b): restituisce la sommasubtract(int a, int b): restituisce la differenza (a - b)multiply(int a, int b): restituisce il prodottopower(int base, int exponent, int multiplier): restituiscemultiplier * (base ^ exponent). Usa un ciclo semplice per calcolare la potenza (supponi che gli esponenti siano non negativi).
Calculator.h: definisci una classeCalculatorche usastd::functionper memorizzare ed eseguire operazioni in modo dinamico.La tua calcolatrice dovrebbe avere:
- Un membro privato
std::function<int(int, int)>per memorizzare l’operazione binaria corrente setOperation(std::function<int(int, int)> op): imposta l’operazione correntecalculate(int a, int b): esegue l’operazione memorizzata e restituisce il risultato
Includi gli header necessari (
<functional>) e assicurati di includere l’header MathOperations.- Un membro privato
main.cpp: leggi tre valori:- Nome dell’operazione:
add,subtract,multiplyoppuresquare - Primo numero (intero)
- Secondo numero (intero)
Crea una
Calculatore configurala in base al nome dell’operazione:- Per
add,subtractemultiply: assegna direttamente alla calcolatrice la funzione corrispondente - Per
square: usastd::bindper creare una versione specializzata dipowerche usi sempre l’esponente 2 e il moltiplicatore 1. La funzione associata dovrebbe accettare due argomenti, di cui solo il primo viene usato come base (il secondo argomento può essere ignorato usando un segnaposto).
Dopo aver impostato l’operazione, chiama
calculate()con i due numeri e stampa:Result: [value]Poi mostra come sostituire le operazioni assegnando alla calcolatrice una lambda che restituisce
a + b + 100, eseguendola con gli stessi valori e stampando:With bonus: [value]- Nome dell’operazione:
Ad esempio, con i valori add, 10 e 5:
Result: 15
With bonus: 115Con i valori multiply, 7 e 3:
Result: 21
With bonus: 110Con i valori square, 4 e 0:
Result: 16
With bonus: 104Questa sfida mostra come std::function offra un modo uniforme per memorizzare diversi tipi di oggetti chiamabili (funzioni regolari, funzioni associate e lambda), mentre std::bind permette di adattare funzioni esistenti fissando alcuni dei loro argomenti.
Provalo tu
#include <iostream>
#include <string>
#include <functional>
#include "Calculator.h"
using namespace std;
int main() {
// Leggi i dati di input
string operation;
int num1, num2;
cin >> operation >> num1 >> num2;
// Crea un'istanza di Calculator
Calculator calc;
// TODO: Configura la calcolatrice in base al nome dell'operazione
// Per "add", "subtract", "multiply": assegna direttamente la funzione corrispondente
// Per "square": usa std::bind per creare una versione specializzata di power
// che usa sempre l'esponente 2 e il moltiplicatore 1
// Suggerimento: usa std::placeholders::_1 per l'argomento della base
if (operation == "add") {
// Il tuo codice qui
} else if (operation == "subtract") {
// Il tuo codice qui
} else if (operation == "multiply") {
// Il tuo codice qui
} else if (operation == "square") {
// Il tuo codice qui - usa std::bind con la funzione power
}
// TODO: Chiama calculate() e stampa il risultato
// Formato: "Result: [value]"
// TODO: Mostra come sostituire le operazioni
// Assegna al calculator una lambda che restituisce a + b + 100
// Chiama di nuovo calculate() e stampa il risultato
// Formato: "With bonus: [value]"
return 0;
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti della programmazione orientata agli oggetti
File esterniBuild e compilazione in C++File header e file sorgenteNamespace e ambitoIntroduzione alla programmazione orientata agli oggetti in C++Classi e oggetti a confrontoIl puntatore 'this'Metodi (funzioni membro)Attributi (membri dati)Fondamenti di costruttori e distruttoriRiepilogo - Calcolatrice semplice4Proprietà delle classi
Membri di istanza e staticiGetter e setterFunzioni membro constParola chiave mutableMetodi e variabili staticiFunzioni e classi friendRiepilogo - Gestore di conti bancari7Ereditarietà
Ereditarietà di baseLivelli di accesso nell’ereditarietàOrdine di chiamata di costruttori e distruttoriRidefinizione dei metodiFunzioni virtuali e VTableEreditarietà multiplaEreditarietà virtualeRiepilogo - Gerarchia dei dipendenti2Gestione della memoria
Memoria Stack vs HeapPuntatori e riferimentiMemoria dinamica (new/delete)Puntatori intelligenti in C++RAII in C++Riepilogo - Gestore di array dinamico5Incapsulamento
Specificatori di accesso in C++Specificatori di accesso in dettaglioOccultamento delle informazioniStruct vs classClassi annidate e interneRiepilogo - Sistema di registrazione degli studenti8Polimorfismo
Polimorfismo a compile time e a runtimeOverload delle funzioniFunzioni virtuali: ripassoFunzioni virtuali pureClassi astratteProgettazione delle interfacce in C++Dynamic casting e RTTIRipasso: calcolatrice di forme11Concetti avanzati di OOP
Composizione vs ereditarietàMixin tramite CRTPIdiom PimplType ErasureEnum class e tipizzazione forteGestione delle eccezioni in OOPGerarchie personalizzate di eccezioni14Pattern di progettazione - Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositeRAII come pattern3Costruttori e distruttori
Costruttore predefinitoCostruttore con parametriCostruttore di copiaCostruttore di spostamentoListe di inizializzazione dei costruttoriCostruttori delegantiApprofondimento sui distruttoriRegola del Tre / Cinque / ZeroRipasso - classe String6Sovraccarico degli operatori
Introduzione al sovraccarico degli operatoriSovraccarico degli operatori aritmeticiSovraccarico degli operatori di confrontoOperatori di flussoSovraccarico dell'operatore di assegnazioneSovraccarico degli operatori [] e ()Operatori di conversione di tipoRipasso - Classe Matrix9Template
Template di funzioneTemplate di classeSpecializzazione dei templateTemplate variadiciBasi di SFINAE e dei trait di tipoRiepilogo - Contenitore generico12Funzionalità moderne di C++
Semantica di spostamento e rvaluePerfect forwardingEspressioni lambda in dettagliostd::function e std::bindconstexpr e constevalStructured bindingsoptional, variant, anyEsercitati da solo: Compilatore C++ online