Type Traits e Concetti (Concepts)
Il grande problema dei template è che, un po’ come nei linguaggi “duck-typed” come Python (“se cammina come una papera e fa quack come una papera, è una papera”), il compilatore si accorge che qualcosa non va solo quando entra dentro la funzione e prova a instanziarla.
Se provassimo a passare un array di puntatori a double alla nostra funzione mean (che cerca di dividerli), la compilazione esploderebbe vomitando schermi interi di errori illeggibili e orripilanti, perché i puntatori non supportano la divisione (/).
Il C++ Moderno offre strumenti per bloccare questi errori sul nascere imponendo vincoli chiari.
Type Traits
Section titled “Type Traits”I Type Traits (Tratti di Tipo), inclusi nell’header <type_traits>, sono librerie preesistenti capaci di esaminare la natura di un tipo durante la compilazione.
Ad esempio std::is_integral<T>::value restituisce true o false a compile-time a seconda che T sia un numero intero. Esistono trait per tutto: is_floating_point, is_pointer, is_copy_constructible, ecc.
La Toppa d’Emergenza: static_assert
Section titled “La Toppa d’Emergenza: static_assert”Poiché i Type Traits si calcolano durante la compilazione, possiamo bloccare a priori un template errato usando lo static_assert.
Lo static_assert fallisce a compile-time, permettendoti di sputare in faccia all’utente incauto un messaggio d’errore leggibile, invece di lasciargli interpretare il crash del compilatore.
Concepts (C++20)
Section titled “Concepts (C++20)”static_assert è una cura provvisoria. Il vero futuro della metaprogrammazione (finalmente standardizzato in C++20) si chiama Concepts (Concetti).
Un Concept è una predicate expression che vive prima ancora di entrare nella funzione. Costringe formalmente un parametro T a possedere certe peculiarità (requires).
Con i Concetti, puoi definire un requisito formale e vincolarlo direttamente nella definizione:
// Sintassi fittizia per mostrare la potenza dei Concepts (C++20)template <typename T>concept Averageable = std::is_default_constructible_v<T> && requires(T a, T b) { { a += b } -> std::same_as<T&>; { a / size_t{1} } -> std::same_as<T>;};
// Sostituiamo il banale 'typename' con il rigoroso Concept 'Averageable'template <Averageable T>T mean(const T* values, size_t length) { // ...}Usando i Concetti, l’errore del compilatore diverrà magistrale: “Errore: Impossibile chiamare mean() con ‘const char’. Il vincolo ‘Averageable’ non è stato soddisfatto.”* I Concetti sono il ponte concettuale che collega la rigidità e l’ordine delle interfacce (Runtime) con l’incredibile potenza dei Template (Compile-time).