Le costanti superflue extra sono cattive dal punto di vista API:
Inserendo costanti superflue extra nel codice per i parametri di tipo intrinseci passati dal valore, la tua API si confonde senza fare promesse significative al chiamante o all'utente API (ostacola solo l'implementazione).
Troppe 'const' in un'API quando non necessarie sono come " piangere lupo ", alla fine le persone inizieranno a ignorare 'const' perché è ovunque e non significa nulla per la maggior parte del tempo.
L'argomento "reductio ad absurdum" per i costi extra in API è buono per questi primi due punti sarebbe se più parametri const fossero buoni, quindi ogni argomento che può avere una const su di esso, DOVREBBE avere una const su di esso. In effetti, se fosse davvero così buono, vorresti che const fosse il valore predefinito per i parametri e che una parola chiave come "mutabile" fosse solo quando vuoi cambiare il parametro.
Quindi proviamo a inserire const ovunque possiamo:
void mungerum(char * buffer, const char * mask, int count);
void mungerum(char * const buffer, const char * const mask, const int count);
Considera la riga di codice sopra. Non solo la dichiarazione è più ingombra, più lunga e più difficile da leggere, ma tre delle quattro parole chiave "const" possono essere tranquillamente ignorate dall'utente API. Tuttavia, l'uso extra di 'const' ha reso la seconda riga potenzialmente PERICOLOSA!
Perché?
Una rapida lettura errata del primo parametro char * const buffer
potrebbe farti pensare che non modificherà la memoria nel buffer di dati che viene passato - tuttavia, questo non è vero! Una "const" superflua può portare a presupposti pericolosi e non corretti sull'API se sottoposti a scansione o letti erroneamente rapidamente.
Le const superflue sono cattive anche dal punto di vista dell'implementazione del codice:
#if FLEXIBLE_IMPLEMENTATION
#define SUPERFLUOUS_CONST
#else
#define SUPERFLUOUS_CONST const
#endif
void bytecopy(char * SUPERFLUOUS_CONST dest,
const char *source, SUPERFLUOUS_CONST int count);
Se FLEXIBLE_IMPLEMENTATION non è vero, l'API promette di non implementare la funzione nel primo modo seguente.
void bytecopy(char * SUPERFLUOUS_CONST dest,
const char *source, SUPERFLUOUS_CONST int count)
{
// Will break if !FLEXIBLE_IMPLEMENTATION
while(count--)
{
*dest++=*source++;
}
}
void bytecopy(char * SUPERFLUOUS_CONST dest,
const char *source, SUPERFLUOUS_CONST int count)
{
for(int i=0;i<count;i++)
{
dest[i]=source[i];
}
}
È una promessa molto sciocca da fare. Perché dovresti fare una promessa che non dà alcun vantaggio al tuo chiamante e limita solo la tua implementazione?
Entrambe sono implementazioni perfettamente valide della stessa funzione, quindi tutto ciò che hai fatto è legato inutilmente una mano dietro la schiena.
Inoltre, è una promessa molto superficiale che è facilmente (e legalmente elusa).
inline void bytecopyWrapped(char * dest,
const char *source, int count)
{
while(count--)
{
*dest++=*source++;
}
}
void bytecopy(char * SUPERFLUOUS_CONST dest,
const char *source,SUPERFLUOUS_CONST int count)
{
bytecopyWrapped(dest, source, count);
}
Senti, l'ho implementato in quel modo, anche se avevo promesso di non farlo, semplicemente usando una funzione wrapper. È come quando il cattivo promette di non uccidere qualcuno in un film e ordina invece al suo scagnozzo di ucciderli.
Quelle costose superflue non valgono niente più che una promessa di un cattivo film.
Ma la capacità di mentire peggiora ancora:
Mi è stato chiarito che è possibile non corrispondere a const in header (dichiarazione) e codice (definizione) usando const spurio. I sostenitori della const-happy affermano che questa è una buona cosa poiché ti consente di inserire const solo nella definizione.
// Example of const only in definition, not declaration
class foo { void test(int *pi); };
void foo::test(int * const pi) { }
Tuttavia, il contrario è vero ... puoi inserire una const spuria solo nella dichiarazione e ignorarla nella definizione. Questo rende solo una const superflua in un'API più una cosa terribile e una bugia orribile - vedi questo esempio:
class foo
{
void test(int * const pi);
};
void foo::test(int *pi) // Look, the const in the definition is so superfluous I can ignore it here
{
pi++; // I promised in my definition I wouldn't modify this
}
Tutto ciò che la const superflua effettivamente fa è rendere il codice dell'implementatore meno leggibile costringendolo a usare un'altra copia locale o una funzione wrapper quando vuole cambiare la variabile o passare la variabile per riferimento non const.
Guarda questo esempio. Qual è più leggibile? È ovvio che l'unica ragione per la variabile extra nella seconda funzione è perché alcuni designer di API hanno lanciato una const superflua?
struct llist
{
llist * next;
};
void walkllist(llist *plist)
{
llist *pnext;
while(plist)
{
pnext=plist->next;
walk(plist);
plist=pnext; // This line wouldn't compile if plist was const
}
}
void walkllist(llist * SUPERFLUOUS_CONST plist)
{
llist * pnotconst=plist;
llist *pnext;
while(pnotconst)
{
pnext=pnotconst->next;
walk(pnotconst);
pnotconst=pnext;
}
}
Speriamo di aver imparato qualcosa qui. La const superflua è un pugno nell'occhio che ingombra l'API, un fastidioso fastidio, una promessa superficiale e insignificante, un ostacolo inutile e occasionalmente porta a errori molto pericolosi.