Storia della questione:
I template sono stati aggiunti a C++ per implementare in modo efficace algoritmi e strutture dati generiche. Fin dall'inizio era necessario un meccanismo di deduzione automatica dei tipi in base agli argomenti di input, per rendere l'utilizzo dei template più comodo per gli sviluppatori.
Problema:
Il meccanismo di deduzione non è sempre ovvio: si presenta ambiguità con i riferimenti, il mascheramento dei tipi, le specializzazioni parziali, i parametri template come riferimenti e le costanti. A volte il compilatore non riesce a dedurre il tipo o produce un risultato inaspettato.
Soluzione:
Il compilatore analizza gli argomenti, li confronta con i parametri template, tenendo conto delle regole dei qualificatori cv, dei riferimenti e dei puntatori. Le carenze e le limitazioni richiedono una specifica esplicita del tipo quando la deduzione automatica non è possibile.
Esempio di codice:
template<typename T> void func(T arg) { /* ... */ } func(10); // T dedotto come int func("abc"); // T dedotto come const char* // Differenza tra T arg e T& arg: template<typename T> void printRef(T& arg); // Non accetterà oggetti temporanei!
Caratteristiche chiave:
Cosa succede se la funzione template accetta T& e si cerca di passarle un oggetto temporaneo?
Il compilatore non potrà dedurre il tipo, poiché un oggetto temporaneo non può essere passato tramite un riferimento non costante. Si verificherà un errore di compilazione.
template<typename T> void foo(T& arg); foo(42); // Errore!
Come funziona la deduzione dei tipi con gli array?
Gli array "decadono" in puntatori quando vengono passati per valore, ma se il template accetta un riferimento, la dimensione dell'array viene mantenuta, cosa che spesso viene utilizzata per implementare template di dimensione sicura.
template<typename T, size_t N> void arraySize(T (&arr)[N]) { std::cout << N; } int x[10]; arraySize(x); // Stampa 10
Perché non è sempre corretto usare auto per memorizzare i risultati delle chiamate a funzioni template?
Auto nella deduzione del tipo può "cortocircuitare" const o qualificatori di riferimento, il che a volte porta a errori imprevisti di copia o mutabilità.
auto x = funcReturningRef(); // x sarà per valore, non riferimento!
Una funzione template di ordinamento universale accetta T&, l'utente prova a passare un oggetto temporaneo. Di conseguenza, il codice non si compila e l'errore mette in difficoltà lo sviluppatore.
Vantaggi:
Svantaggi:
L'ordinatore è implementato tramite riferimenti universali (T&&) utilizzando std::forward, il che consente di lavorare correttamente sia con lvalue che con rvalue, aumentando così le prestazioni e la flessibilità.
Vantaggi:
Svantaggi: