Template di Funzione

I template permettono di scrivere codice generico che funziona con qualsiasi tipo di dato. Il compilatore genera il codice specifico per ogni tipo usato.

C++Function templates
#include <iostream>
#include <string>
using namespace std;

// Template di funzione: T è un tipo generico
template <typename T>
T massimo(T a, T b) {
    return (a > b) ? a : b;
}

// Template con più tipi
template <typename T, typename U>
void stampa_coppia(T a, U b) {
    cout << "(" << a << ", " << b << ")\n";
}

// Specializzazione esplicita per const char*
template <>
const char* massimo(const char *a, const char *b) {
    return (strlen(a) > strlen(b)) ? a : b;
}

// Template con parametro non-tipo
template <int N>
void ripeti(const string &s) {
    for (int i = 0; i < N; i++) cout << s << " ";
    cout << "\n";
}

int main() {
    cout << massimo(10, 20) << "\n";          // int
    cout << massimo(3.14, 2.71) << "\n";      // double
    cout << massimo('A', 'Z') << "\n";        // char
    cout << massimo("ciao", "mondo!") << "\n"; // specializzato

    stampa_coppia(42, "hello");
    stampa_coppia(3.14, true);

    ripeti<5>("Echo");

    return 0;
}

Container STL

vector — Array dinamico

C++std::vector
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main() {
    vector<int> v = {5, 2, 8, 1, 9, 3};

    v.push_back(7);         // Aggiunge in coda
    v.insert(v.begin(), 0); // Inserisce in posizione

    cout << "Size: " << v.size() << ", Capacity: " << v.capacity() << "\n";

    // Range-based for (C++11)
    for (int n : v) cout << n << " ";
    cout << "\n";

    // Algoritmi STL
    sort(v.begin(), v.end());                   // Ordina
    cout << "Ordinato: ";
    for (int n : v) cout << n << " ";
    cout << "\n";

    auto it = find(v.begin(), v.end(), 5);      // Cerca
    if (it != v.end())
        cout << "5 trovato a posizione " << (it - v.begin()) << "\n";

    // Lambda con find_if
    auto pari = find_if(v.begin(), v.end(), [](int n){ return n % 2 == 0; });
    cout << "Primo pari: " << *pari << "\n";

    int somma = 0;
    for_each(v.begin(), v.end(), [&somma](int n){ somma += n; });
    cout << "Somma: " << somma << "\n";

    v.erase(v.begin() + 2);  // Rimuove l'elemento a indice 2
    v.pop_back();             // Rimuove l'ultimo

    return 0;
}

map e unordered_map — Dizionari

C++std::map e std::unordered_map
#include <iostream>
#include <map>
#include <unordered_map>
#include <string>
using namespace std;

int main() {
    // map: ordinato per chiave O(log n)
    map<string, int> punteggi;
    punteggi["Mario"]  = 95;
    punteggi["Giulia"] = 88;
    punteggi["Luca"]   = 72;
    punteggi["Anna"]   = 95;

    // Iterazione ordinata per chiave
    for (const auto &[nome, punteggio] : punteggi) {
        cout << nome << ": " << punteggio << "\n";
    }

    // Cerca
    if (punteggi.count("Mario"))
        cout << "Mario: " << punteggi.at("Mario") << "\n";

    // Rimuovi
    punteggi.erase("Luca");

    // unordered_map: non ordinato, O(1) medio con hash
    unordered_map<string, int> frequenza;
    string testo = "il gatto sul tetto il gatto mangia il topo";
    istringstream ss(testo);
    string parola;
    while (ss >> parola) frequenza[parola]++;

    cout << "\nFrequenza parole:\n";
    for (const auto &[w, f] : frequenza)
        cout << "  " << w << ": " << f << "\n";

    return 0;
}

set, queue, stack e altri

C++set, queue, stack, pair, tuple
#include <iostream>
#include <set>
#include <queue>
#include <stack>
#include <utility>   // pair
#include <tuple>
using namespace std;

int main() {
    // set: elementi unici, ordinati
    set<int> s = {5, 2, 8, 2, 1, 5, 9};
    for (int n : s) cout << n << " ";  // 1 2 5 8 9
    cout << "\n";

    s.insert(3);
    cout << "3 nel set? " << (s.count(3) ? "Sì" : "No") << "\n";

    // queue: FIFO
    queue<string> coda;
    coda.push("primo"); coda.push("secondo"); coda.push("terzo");
    while (!coda.empty()) {
        cout << coda.front() << " ";
        coda.pop();
    }
    cout << "\n";

    // stack: LIFO
    stack<int> pila;
    pila.push(1); pila.push(2); pila.push(3);
    while (!pila.empty()) { cout << pila.top() << " "; pila.pop(); }
    cout << "\n";

    // pair
    pair<string, int> p = {"ciao", 42};
    cout << p.first << " " << p.second << "\n";

    // tuple (C++11)
    auto t = make_tuple("Mario", 25, 3.14);
    cout << get<0>(t) << ", " << get<1>(t) << ", " << get<2>(t) << "\n";

    return 0;
}

Algoritmi STL e Lambda

C++algorithm — sort, find, transform, reduce
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>   // accumulate, reduce
#include <string>
using namespace std;

int main() {
    vector<int> v = {3,1,4,1,5,9,2,6,5,3,5};

    // sort con comparatore custom
    sort(v.begin(), v.end(), [](int a, int b){ return a > b; }); // Decrescente
    for (int n : v) cout << n << " ";
    cout << "\n";

    // unique: rimuove duplicati consecutivi (dopo sort)
    sort(v.begin(), v.end());
    auto ultimo = unique(v.begin(), v.end());
    v.erase(ultimo, v.end());
    cout << "Senza duplicati: ";
    for (int n : v) cout << n << " ";
    cout << "\n";

    // transform: applica funzione a ogni elemento
    vector<int> quadrati(v.size());
    transform(v.begin(), v.end(), quadrati.begin(),
              [](int n){ return n * n; });
    cout << "Quadrati: ";
    for (int n : quadrati) cout << n << " ";
    cout << "\n";

    // accumulate: somma (o operazione custom)
    int somma = accumulate(v.begin(), v.end(), 0);
    int prodotto = accumulate(v.begin(), v.end(), 1, [](int a, int b){ return a*b; });
    cout << "Somma: " << somma << ", Prodotto: " << prodotto << "\n";

    // count_if
    int pari = count_if(v.begin(), v.end(), [](int n){ return n%2==0; });
    cout << "Numeri pari: " << pari << "\n";

    // any_of, all_of, none_of
    bool tutti_pos  = all_of(v.begin(), v.end(), [](int n){ return n>0; });
    bool qualche_gt5 = any_of(v.begin(), v.end(), [](int n){ return n>5; });
    cout << "Tutti > 0? " << tutti_pos << ", Qualcuno > 5? " << qualche_gt5 << "\n";

    // min_element, max_element
    auto minIt = min_element(v.begin(), v.end());
    auto maxIt = max_element(v.begin(), v.end());
    cout << "Min: " << *minIt << ", Max: " << *maxIt << "\n";

    return 0;
}

Smart Pointer (C++11)

I smart pointer gestiscono automaticamente la memoria: chiamano delete quando non servono più, eliminando i memory leak.

C++unique_ptr, shared_ptr, weak_ptr
#include <iostream>
#include <memory>
#include <vector>
using namespace std;

class Risorsa {
    string nome;
public:
    Risorsa(const string &n) : nome(n) { cout << "Creato: " << nome << "\n"; }
    ~Risorsa()                          { cout << "Distrutto: " << nome << "\n"; }
    void usa() const { cout << "Uso: " << nome << "\n"; }
};

int main() {
    // unique_ptr: UNICO proprietario, non copiabile
    {
        auto ptr = make_unique<Risorsa>("unico");
        ptr->usa();
        // Automaticamente distrutto alla fine del blocco
    }
    cout << "Dopo il blocco\n\n";

    // shared_ptr: proprietà CONDIVISA (reference counting)
    shared_ptr<Risorsa> sp1 = make_shared<Risorsa>("condiviso");
    cout << "Conteggio: " << sp1.use_count() << "\n";  // 1
    {
        shared_ptr<Risorsa> sp2 = sp1;  // Copia: conteggio aumenta
        cout << "Conteggio: " << sp1.use_count() << "\n";  // 2
        sp2->usa();
    }
    // sp2 distrutto → conteggio torna a 1
    cout << "Conteggio dopo blocco: " << sp1.use_count() << "\n";  // 1

    // Uso tipico in vector
    vector<unique_ptr<Risorsa>> risorse;
    risorse.push_back(make_unique<Risorsa>("A"));
    risorse.push_back(make_unique<Risorsa>("B"));
    risorse.push_back(make_unique<Risorsa>("C"));
    for (const auto &r : risorse) r->usa();
    // Tutti automaticamente distrutti alla fine del main

    return 0;
}
🏋️ Esercizi Finali
Esercizi che combinano template, STL e algoritmi avanzati.
1
Frequenza di caratteri
Facilemap / algoritmi

Leggi una stringa e usa map<char, int> per contare la frequenza di ogni carattere (ignorando maiuscole/minuscole). Ordina e stampa i caratteri per frequenza decrescente usando un vector<pair<char,int>>.

C++
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
#include <string>
#include <cctype>
using namespace std;

int main() {
    string testo;
    cout << "Inserisci testo: ";
    getline(cin, testo);

    map<char, int> freq;
    for (char c : testo) {
        if (isalpha(c)) freq[tolower(c)]++;
    }

    vector<pair<char,int>> v(freq.begin(), freq.end());
    sort(v.begin(), v.end(), [](const auto &a, const auto &b){
        return a.second > b.second;  // Ordine decrescente
    });

    for (const auto &[c, f] : v) {
        cout << c << ": " << string(f, '#') << " (" << f << ")\n";
    }
}
2
Template Pair personalizzato
MedioTemplate / classi

Implementa una classe template Coppia<T, U> simile a std::pair con: costruttore, getter, swap() (scambia i valori), operator==, operator<<. Poi crea una funzione template zip(v1, v2) che unisca due vettori in un vettore di coppie.

C++
#include <iostream>
#include <vector>
#include <string>
using namespace std;

template <typename T, typename U>
class Coppia {
    T primo;
    U secondo;
public:
    Coppia(T p, U s) : primo(p), secondo(s) {}
    T getFirst()  const { return primo; }
    U getSecond() const { return secondo; }

    void swap() { T tmp = primo; primo = secondo; secondo = tmp; }  // solo se T==U

    bool operator==(const Coppia &c) const {
        return primo == c.primo && secondo == c.secondo;
    }

    friend ostream& operator<<(ostream &os, const Coppia &c) {
        return os << "(" << c.primo << ", " << c.secondo << ")";
    }
};

template <typename T, typename U>
vector<Coppia<T,U>> zip(const vector<T> &v1, const vector<U> &v2) {
    vector<Coppia<T,U>> result;
    int n = min(v1.size(), v2.size());
    for (int i = 0; i < n; i++)
        result.push_back({v1[i], v2[i]});
    return result;
}

int main() {
    Coppia<string, int> c1("ciao", 42);
    Coppia<string, int> c2("mondo", 7);
    cout << c1 << "\n" << c2 << "\n";
    cout << "Uguali? " << (c1 == c2 ? "Sì" : "No") << "\n";

    vector<string> nomi = {"Mario", "Giulia", "Luca"};
    vector<int> voti = {28, 30, 25};
    auto zipped = zip(nomi, voti);
    for (const auto &c : zipped) cout << c << "\n";
}
3
Grafo con lista di adiacenza
DifficileSTL / BFS / DFS

Implementa un grafo non orientato usando unordered_map<int, vector<int>>. Implementa: aggiungi_nodo, aggiungi_arco, bfs(start) (visita in ampiezza con queue), dfs(start) (visita in profondità con stack), percorso_minimo(src, dst) (BFS + ricostruzione del cammino).

C++
#include <iostream>
#include <unordered_map>
#include <vector>
#include <queue>
#include <stack>
#include <set>
using namespace std;

class Grafo {
    unordered_map<int, vector<int>> adj;
public:
    void aggiungi_arco(int u, int v) {
        adj[u].push_back(v);
        adj[v].push_back(u);
    }

    void bfs(int start) {
        set<int> visitati;
        queue<int> q;
        q.push(start);
        visitati.insert(start);
        cout << "BFS: ";
        while (!q.empty()) {
            int n = q.front(); q.pop();
            cout << n << " ";
            for (int vic : adj[n]) {
                if (!visitati.count(vic)) {
                    visitati.insert(vic);
                    q.push(vic);
                }
            }
        }
        cout << "\n";
    }

    void dfs(int start) {
        set<int> visitati;
        stack<int> s;
        s.push(start);
        cout << "DFS: ";
        while (!s.empty()) {
            int n = s.top(); s.pop();
            if (visitati.count(n)) continue;
            visitati.insert(n);
            cout << n << " ";
            for (int vic : adj[n]) {
                if (!visitati.count(vic)) s.push(vic);
            }
        }
        cout << "\n";
    }

    vector<int> percorso(int src, int dst) {
        unordered_map<int,int> parent;
        set<int> visitati;
        queue<int> q;
        q.push(src); visitati.insert(src); parent[src] = -1;
        while (!q.empty()) {
            int n = q.front(); q.pop();
            if (n == dst) {
                vector<int> path;
                for (int x = dst; x != -1; x = parent[x]) path.push_back(x);
                reverse(path.begin(), path.end());
                return path;
            }
            for (int vic : adj[n]) {
                if (!visitati.count(vic)) {
                    visitati.insert(vic);
                    parent[vic] = n;
                    q.push(vic);
                }
            }
        }
        return {};
    }
};

int main() {
    Grafo g;
    g.aggiungi_arco(1, 2); g.aggiungi_arco(1, 3);
    g.aggiungi_arco(2, 4); g.aggiungi_arco(3, 5);
    g.aggiungi_arco(4, 6); g.aggiungi_arco(5, 6);

    g.bfs(1);
    g.dfs(1);

    auto path = g.percorso(1, 6);
    cout << "Percorso 1→6: ";
    for (int n : path) cout << n << " ";
    cout << "\n";
}
4
Pipeline funzionale
DifficileLambda / functional / avanzato

Crea una classe Pipeline<T> che permetta di concatenare trasformazioni su un vettore: filter(pred), map(func), reduce(func, init), take(n). Deve supportare il metodo chaining: Pipeline(v).filter(...).map(...).reduce(...). Esempio: prendi i numeri 1..20, filtra i pari, raddoppiali, somma i primi 5.

C++
#include <iostream>
#include <vector>
#include <functional>
#include <algorithm>
#include <numeric>
using namespace std;

template <typename T>
class Pipeline {
    vector<T> dati;
public:
    Pipeline(vector<T> v) : dati(move(v)) {}

    Pipeline<T> filter(function<bool(T)> pred) {
        vector<T> res;
        copy_if(dati.begin(), dati.end(), back_inserter(res), pred);
        return Pipeline<T>(res);
    }

    template <typename U>
    Pipeline<U> map(function<U(T)> f) {
        vector<U> res;
        transform(dati.begin(), dati.end(), back_inserter(res), f);
        return Pipeline<U>(res);
    }

    Pipeline<T> take(int n) {
        int sz = min((int)dati.size(), n);
        return Pipeline<T>(vector<T>(dati.begin(), dati.begin() + sz));
    }

    T reduce(function<T(T,T)> f, T init) {
        return accumulate(dati.begin(), dati.end(), init, f);
    }

    void print() const {
        cout << "[ ";
        for (const auto &x : dati) cout << x << " ";
        cout << "]\n";
    }

    vector<T> toVector() const { return dati; }
};

int main() {
    // Genera 1..20
    vector<int> numeri(20);
    iota(numeri.begin(), numeri.end(), 1);

    // Pipeline: filtra pari → raddoppia → prendi i primi 5 → somma
    int risultato = Pipeline<int>(numeri)
        .filter([](int n){ return n % 2 == 0; })
        .map<int>([](int n){ return n * 2; })
        .take(5)
        .reduce([](int a, int b){ return a + b; }, 0);

    cout << "Risultato: " << risultato << "\n";

    // Visualizza ogni passo
    Pipeline<int>(numeri)
        .filter([](int n){ return n % 2 == 0; })
        .map<int>([](int n){ return n * 2; })
        .take(5)
        .print();
}
🎉

Guida Completata!

Hai percorso tutta la guida da Hello World fino alle STL avanzate di C++. Continua a praticare con esercizi su codeforces.com, leetcode.com o codeabbey.com.

Torna all'indice →