Omar Game Developer
Const ref in C++

Const ref in C++

tips programming C++ Unreal Engine

Prima di iniziare, anche se quello che scriverò qui riguarda specificamente C++ (e la sua applicazione in Unreal), il concetto può essere esteso praticamente a qualsiasi linguaggio.

Recentemente mi sono imbattuto in un codice che era più o meno così:

void MethodName(const FString& param1, const float& param2, const bool& param3)

Sentiamo spesso dire che tutto ciò che è per riferimento è meglio, dopo tutto evita di creare una copia della variabile che è stata passata come argomento, e se è “const” allora molto meglio. È così?

Mi dispiace essere portatore di cattive notizie: no, non sempre.

Mentre nella stragrande maggioranza dei casi sarà “meglio” (o la cosa giusta da fare) e mentre è preferibile fare tutto “ref-const” e non passare per valore, qualcosa che dovremmo passare per riferimento o non passare come const qualcosa che non abbiamo intenzione di modificare, la realtà è che ci sono casi in cui questo non è corretto, fondamentalmente perché distrugge ciò che il programmatore voleva ottenere all’inizio: massimizzare le risorse.

E non mi riferisco a situazioni logiche come:

  • Volevamo modificare il valore del parametro all’interno del corpo del metodo/funzione (const darebbe un errore di compilazione)
  • Volevamo che il valore del parametro modificato non modificasse il valore della variabile originariamente passata come argomento (passare per riferimento non ci permette, almeno direttamente, quel comportamento).

Mi riferisco a situazioni in cui non volevamo una cosa o l’altra, e tuttavia è ancora il meno performante, in quel modo. Perché?

Vediamo… quando un valore viene passato per riferimento non stiamo passando il valore, stiamo passando un puntatore che punta alla posizione di memoria della variabile (dopo tutto, il riferimento è un alias che sta semplicemente referenziando un puntatore, uno spazio di memoria).

Un puntatore ha una dimensione che varia a seconda dell’architettura del sistema operativo, ma è sempre la stessa per l’architettura data. In un’architettura a 64 bit (la più comune oggi) un puntatore occupa 64 bit, che è lo stesso di 8 byte (è vero che ci possono essere eccezioni alla regola e che il compilatore può giocare un ruolo in questo, ma ci atterremo alle regole generali).

Se ci fermiamo di nuovo a guardare i parametri del nostro metodo, ce ne sono 2 che voglio sottolineare:

const float& param2,
const bool& param3

Quanto occupa un float in C++? 4 byte

E un bool? È definito dall’implementazione (con sizeof() puoi scoprire quanto è nel tuo, minimo 1 byte), ma per questo caso particolare impazziamo e diciamo che è 4 byte.

Come puoi vedere, non stiamo guadagnando prestazioni (in questo caso nell’utilizzo della memoria), oltre ad essere costretti a dover “gestire” un riferimento.

Inoltre, possiamo continuare ad aggiungere problemi inutili (questa volta passiamo per riferimento, ma non può essere const), come spiegato da “The Cherno” in questa parte di un video.

Perché non passare semplicemente per valore? Non c’è un vero “perché”, sarebbe la migliore pratica in questo caso.

Lo stesso si può dire per le strutture (non solo primitive) le cui variabili membro sommano meno di 8 byte (e non hanno un costruttore di copia che fa “cose strane” o qualcosa del genere).

Come regola generale, non uso riferimenti in nessun tipo built-in “sotto” la dimensione di un double, né in strutture che soddisfano le condizioni menzionate. Menzione speciale per i valori “out” che devo restituire dalla funzione, chiaramente dobbiamo passarli per riferimento (non const). Ma come ho già ripetuto più volte, non intendo cadere in tutti i casi speciali in cui, anche con quanto già menzionato, sarebbe meglio usare un riferimento.

Certo, nella stragrande maggioranza dei casi useremo il riferimento e (quando appropriato) const. L’idea non è demonizzare la pratica (e come ho detto, è preferibile “sbagliare” da questa parte e non dall’altra). L’idea è semplicemente non cadere nei martelli d’oro e nel “perché ho visto che qualcuno l’ha scritto così deve essere giusto”, in generale non ho mai trovato “formule” che funzionano nel 100% dei casi e ragionare sempre su ciò che stiamo facendo porta a conclusioni migliori, anche se è solo per risparmiare 4 byte di memoria.

Crediti: Immagine di DCStudio su Freepik