Nelle lezioni precedenti, trattando i qualificatori const e mutable, abbiamo introdotto il concetto di const-correctness in C++.
L'operatore di conversione di tipi const
completa l'insieme di strumenti sintattici a disposizione del programmatore per la corretta implementazione delle politiche di accesso alle entità immutabili del programma.
In particolare, l'operatore const_cast
consente di eliminare il vincolo di accesso in sola lettura per un puntatore o reference ad una variabile const-qualified.
Ovviamente, nel caso ideale, non dovrebbe mai essere necessario ricorrere a questo tipo di conversione. Infatti, infrangere un tale vincolo può avere degli effetti prevedibilmente indesiderati per l'esecuzione di un programma. Tuttavia, questo operatore è parte della specifica del linguaggio poiché, come vedremo, in determinati contesti esso può essere applicato in sicurezza e contribuire indirettamente all'impiego di politiche di const-correctness.
Il listato seguente mostra l'applicazione dell'operatore const_cast
in due casi molto differenti tra loro da un punto di vista semantico.
#include <iostream>
#include <iomanip>
const float PI = 3.14f;
void printFloat(float* number, int precision)
{
std::cout << std::fixed
<< std::setprecision(precision)
<< *number
<< std::endl;
}
void incrementFloat(float* number)
{
(*number)++;
}
int main()
{
// Ok
printFloat(const_cast<float*>(&PI), 5);
// Comportamento indefinito
incrementFloat(const_cast<float*>(&PI));
return 0;
}
In questo programma d'esempio abbiamo la dichiarazione di una costante numerica, il pi greco, e due funzioni di utilità che operano su numeri in virgola mobile. La prima consente di stampare un numero decimale con la precisione numerica desiderata; la seconda lo incrementa di 1.
L'operatore di conversione di tipo costante viene qui impiegato per passare come argomento la costante numerica PI ad entrambe le funzioni.
Sebbene da un punto di vista meramente sintattico, l'uso dell'operatore const_cast
sia lecito in entrambi i casi, esiste una differenza sostanziale tra i due scenari. Nel primo infatti la funzione printFloat non altera il valore del suo argomento, ed in quest'ottica, l'uso dell'operatore di conversione è un espediente sintattico che rende compatibile anche un argomento const-qualified con l'uso di questa funzione.
Nel secondo caso invece, poiché la funzione incrementFloat modifica il proprio argomento, abbiamo una violazione implicita di un vincolo semantico che può facilmente degenerare in una condizione di errore. Gli effetti derivanti dalla modifica di una variabile costante in C++ ricadono infatti nella definizione di comportamento indefinito. Nel caso specifico, per via delle ottimizzazioni applicate da alcuni compilatori, l'invocazione di incrementFloat può dare luogo ad una violazione di accesso alla memoria.
Usi leciti di const_cast
In generale, aggirare il qualificatore const
è sempre una cattiva idea. Nella pratica però, come abbiamo visto per il qualificatore mutable
, esistono dei casi in cui è indispensabile ricorrere all'uso dell'operatore const_cast
per salvaguardare la const-correctness di un modello dati.
In particolare, l'uso del qualificatore const
può creare dei problemi quando si integra una libreria o una code base di terze parti che non implementa alcuna politica di const-correctness. In questo caso l'operatore di conversione const_cast
, se usato con criterio, può essere un ausilio all'integrazione. In casi eccezionali inoltre, l'operatore const_cast
può essere applicato al puntatore this nei metodi const di una classe per consentire la modifica di un dato membro, come alternativa all'uso di mutable
.
In entrambi gli scenari è tuttavia onere del programmatore assicurare che le variabili costanti non siano modificate, o che eventuali alterazioni non diano luogo a comportamenti inattesi.
Poiché non è mai semplice fornire queste garanzie, l'uso dell'operatore const_cast
dovrebbe essere considerato solo in casi estremi e dopo una attenta analisi delle sue ripercussioni sul resto del programma.
const_cast e volatile
L'operatore const_cast
può essere impiegato anche per rimuovere il qualificatore volatile dalla definizione del tipo di una variabile.
Questo particolare caso d'uso è di rara utilità nel contesto di applicazioni scritte facendo riferimento alle versioni più aggiornate dello standard del linguaggio.
Tuttavia, per ragioni di retro-compatibilità questa funzionalità è ancora oggi disponibile ed applicabile laddove necessario.