Skip to content

Dichiarazioni, Funzioni e Namespaces

Le Declaration Statements (Dichiarazioni) introducono identificatori — come funzioni, classi, template e namespace — all’interno del programma. (Anche lo static_assert introdotto nel Capitolo 6 rientra formalmente nella categoria delle dichiarazioni).

La Dichiarazione di una funzione (o prototipo / firma) ne specifica gli input e gli output. Non ha bisogno di specificare il nome dei parametri, ma solo i tipi.

void randomize(uint32_t&); // Dichiarazione

La Definizione di una funzione include sia la dichiarazione che il blocco di codice (il corpo) della funzione stessa.

Il C++ (a differenza di linguaggi di scripting lineari) permette di dichiarare funzioni e usarle nel codice prima ancora di averle definite. Finché il compilatore trova il prototipo, saprà come validare la chiamata e confiderà nel fatto che la definizione verrà fornita altrove (magari alla fine del file, o in un altro file .cpp processato dal linker).

Questo pattern è utilissimo (e onnipresente) nei progetti reali che usano molteplici file, in quanto la dichiarazione va solitamente nell’Header file (.h), mentre la definizione cicciotta va nel Source file (.cpp).

I progetti reali importano centinaia di librerie esterne. È matematicamente probabile che qualcuno prima di te abbia scritto una classe chiamata Database o User. Per prevenire disastrosi conflitti di nome (Naming Conflicts), il C++ incapsula gli identificatori nei Namespaces.

Per impostazione predefinita, tutto finisce nel global namespace. Ma possiamo inserire il nostro codice in stanze chiuse:

namespace BroopKidron13 {
namespace Shaltanac {
enum class Color { Mauve, Pink, Russet };
}
}
// Sintassi moderna (C++17) molto più compatta:
namespace BroopKidron13::Shaltanac {
enum class Color { Mauve, Pink, Russet };
}

Per accedere al colore Mauve da fuori, dovrai specificare il percorso assoluto usando l’operatore di scope-resolution ::. Il nome diventerà enorme: BroopKidron13::Shaltanac::Color::Mauve.

Per alleviare il tunnel carpale, puoi usare la direttiva using per importare un determinato simbolo (o l’intero namespace) nel tuo scope corrente.

PERICOLO ASSOLUTO: Non inserire MAI using namespace XYZ; all’interno di un file Header (.h o .hpp). Qualsiasi altro file .cpp del progetto che includerà il tuo header si ritroverà l’intero global namespace “inquinato” e fuso con XYZ, causando conflitti di nome catastrofici in punti inaspettati. Usalo solo internamente ai blocchi o nei .cpp.

Se il nome di un tipo è troppo lungo e scocciante da digitare ripetutamente, puoi creare un Type Alias (Alias di tipo). Usa la keyword using seguita da un =. È l’evoluzione C++ del vecchio typedef del C.

L’Alias agisce come un sinonimo e non genera un nuovo tipo a livello di runtime.

// Rinomina un array in modo leggibile
using String = const char[260];
// Semplifica path chilometrici
using ShaltanacColor = BroopKidron13::Shaltanac::Color;
int main() {
String message { "Il mio colore preferito è..." };
ShaltanacColor my_color = ShaltanacColor::Mauve;
}

Gli Alias brillano in sinergia con i Template, dove puoi effettuare “l’applicazione parziale”: ovvero definire un alias che congela il primo parametro di template lasciando dinamico il secondo, abbattendo la verbosità del codice meta-programmato.