Template di funzione
Fa parte della sezione Programmazione orientata agli oggetti del percorso C++ di Coddy. Lezione 64 di 104.
Immagina di scrivere una funzione per trovare il massimo tra due numeri interi, per poi renderti conto che ti serve la stessa logica per i numeri double e, ancora, per le stringhe. Senza i template, dovresti scrivere funzioni quasi identiche per ogni tipo. I template di funzione risolvono il problema consentendoti di scrivere la logica una sola volta e di fare in modo che il compilatore generi automaticamente versioni specifiche per ciascun tipo.
Un modello di funzione usa la parola chiave template seguita dai parametri del modello tra parentesi angolari:
template <typename T>
T maximum(T a, T b) {
return (a > b) ? a : b;
}
int main() {
std::cout << maximum(5, 3) << std::endl; // Usa la versión int
std::cout << maximum(3.14, 2.71) << std::endl; // Usa la versión double
std::cout << maximum('a', 'z') << std::endl; // Usa la versión char
}Il compilatore esamina ogni chiamata e genera una funzione concreta per quel tipo specifico. Questo processo si chiama istanziazione del template. Puoi anche specificare esplicitamente il tipo quando necessario:
std::cout << maximum<double>(5, 3.14) << std::endl; // Forza la versione doubleI modelli possono avere più parametri di tipo, consentendo progetti ancora più flessibili:
template <typename T, typename U>
void printPair(T first, U second) {
std::cout << first << ", " << second << std::endl;
}
printPair(42, "hello"); // T=int, U=const char*
printPair(3.14, 100); // T=double, U=intI modelli di funzione forniscono polimorfismo in fase di compilazione: il tipo viene determinato quando il codice viene compilato, non durante l'esecuzione. Ciò significa zero overhead durante l'esecuzione rispetto alle funzioni virtuali, rendendo i modelli ideali per il codice generico in cui le prestazioni sono fondamentali.
Sfida
FacileCostruiamo un kit di utilità usando i template di funzione per creare operazioni riutilizzabili che funzionano con qualsiasi tipo compatibile. Organizzerai le tue funzioni generiche in un file header e ne dimostrerai la flessibilità nel programma principale.
Creerai due file:
MathUtils.h: Definisci una raccolta di template di funzione che eseguono operazioni comuni:minimum: una funzione template che accetta due valori dello stesso tipo e restituisce quello più piccolo.clamp: una funzione template che accetta tre parametri: un valore, un limite inferiore e un limite superiore. Restituisce il valore vincolato entro i limiti (restituisce il limite inferiore se il valore è minore del limite inferiore, il limite superiore se il valore è maggiore del limite superiore, altrimenti restituisce il valore stesso).swapValues: una funzione template che accetta due riferimenti dello stesso tipo e scambia i loro valori.main.cpp: Leggi sei input (ciascuno su una riga separata):- Primo intero
- Secondo intero
- Un valore double da limitare
- Limite inferiore (double)
- Limite superiore (double)
- Un carattere
Dimostra i tuoi template:
- Trovando il minimo dei due interi e stampando:
Min of <a> and <b>: <result> - Trovando il minimo dei caratteri
'm'e del carattere inserito, stampando:Min of m and <char>: <result> - Limitando il valore double e stampando:
Clamp <value> to [<low>, <high>]: <result> - Limitando il primo intero all'intervallo [0, 100] e stampando:
Clamp <value> to [0, 100]: <result> - Scambiando i due interi e stampando:
After swapValues: <a>, <b>
Ad esempio, con gli input 25, 10, 3.7, 1.0, 5.0 e z:
Min of 25 and 10: 10
Min of m and z: m
Clamp 3.7 to [1, 5]: 3.7
Clamp 25 to [0, 100]: 25
After swapValues: 10, 25Nota come ogni funzione template funzioni senza problemi con interi, double e caratteri: il compilatore genera la versione appropriata per ogni tipo utilizzato. La tua funzione swapValues dovrebbe modificare le variabili originali tramite riferimenti, dimostrando che i template funzionano con parametri di riferimento proprio come le funzioni normali.
Provalo tu
#include <iostream>
#include "MathUtils.h"
using namespace std;
int main() {
// Leggi gli input
int a, b;
double value, low, high;
char ch;
cin >> a;
cin >> b;
cin >> value;
cin >> low;
cin >> high;
cin >> ch;
// TODO: Usa il template minimum per trovare il minimo tra due numeri interi
// Stampa: "Min of <a> and <b>: <result>"
// TODO: Usa il template minimum per trovare il minimo tra 'm' e il carattere di input
// Stampa: "Min of m and <char>: <result>"
// TODO: Usa il template clamp sul valore double
// Stampa: "Clamp <value> to [<low>, <high>]: <result>"
// TODO: Usa il template clamp sul primo numero intero nell'intervallo [0, 100]
// Stampa: "Clamp <value> to [0, 100]: <result>"
// TODO: Usa il template swapValues sui due numeri interi
// Stampa: "After swapValues: <a>, <b>"
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 genericoEsercitati da solo: Compilatore C++ online