Const extra supérflua é ruim do ponto de vista da API:
Colocar constantes supérfluas extras em seu código para parâmetros intrínsecos passados por valor atrapalha sua API, sem fazer nenhuma promessa significativa ao chamador ou usuário da API (apenas prejudica a implementação).
Excesso de 'const' em uma API quando não é necessário é como " lobo chorando "; eventualmente, as pessoas começarão a ignorar 'const' porque está em todo lugar e não significa nada na maioria das vezes.
O argumento "reductio ad absurdum" para considerações extras na API é bom, pois esses dois primeiros pontos seriam: se mais parâmetros const forem bons, todos os argumentos que possam ter uma const, devem ter uma const. De fato, se fosse realmente tão bom, você desejaria que const seja o padrão para parâmetros e tenha uma palavra-chave como "mutável" somente quando desejar alterar o parâmetro.
Então, vamos tentar colocar const sempre que possível:
void mungerum(char * buffer, const char * mask, int count);
void mungerum(char * const buffer, const char * const mask, const int count);
Considere a linha de código acima. A declaração não é apenas mais confusa, longa e difícil de ler, mas três das quatro palavras-chave 'const' podem ser ignoradas com segurança pelo usuário da API. No entanto, o uso extra de 'const' tornou a segunda linha potencialmente PERIGOSA!
Por quê?
Uma rápida leitura incorreta do primeiro parâmetro char * const buffer
pode fazer você pensar que ele não modificará a memória no buffer de dados que é passado - no entanto, isso não é verdade! 'Const' supérfluo pode levar a suposições perigosas e incorretas sobre sua API quando digitalizadas ou mal interpretadas rapidamente.
Const supérfluo também é ruim do ponto de vista de implementação de código:
#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 não for verdadeiro, a API é "promissora" para não implementar a função da primeira maneira abaixo.
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];
}
}
Essa é uma promessa muito boba de se fazer. Por que você deve fazer uma promessa que não oferece benefício algum ao seu interlocutor e limita apenas sua implementação?
Ambas são implementações perfeitamente válidas da mesma função; portanto, tudo o que você fez foi amarrar uma mão pelas costas desnecessariamente.
Além disso, é uma promessa muito superficial que é fácil (e contornada legalmente).
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);
}
Olha, eu implementei dessa maneira de qualquer maneira, apesar de prometer não fazê-lo - apenas usando uma função de wrapper. É como quando o bandido promete não matar alguém em um filme e ordena que seu capanga o mate.
Essas constantes supérfluas não valem mais do que a promessa de um bandido de cinema.
Mas a capacidade de mentir fica ainda pior:
Fui informado de que você pode incompatível const no cabeçalho (declaração) e código (definição) usando const espúria. Os advogados const-happy afirmam que isso é uma coisa boa, pois permite que você coloque const apenas na definição.
// Example of const only in definition, not declaration
class foo { void test(int *pi); };
void foo::test(int * const pi) { }
No entanto, o inverso é verdadeiro ... você pode colocar uma const espúria apenas na declaração e ignorá-la na definição. Isso torna a const supérflua em uma API apenas uma coisa terrível e uma mentira horrível - veja este exemplo:
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
}
Tudo o que a const supérflua realmente faz é tornar o código do implementador menos legível, forçando-o a usar outra cópia local ou uma função de wrapper quando ele deseja alterar a variável ou passar a variável por referência não const.
Veja este exemplo. Qual é mais legível? É óbvio que a única razão para a variável extra na segunda função é porque algum designer de API lançou uma const supérflua?
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;
}
}
Espero que tenhamos aprendido algo aqui. Const supérfluo é um desagradável desordem da API, uma chatice irritante, uma promessa superficial e sem sentido, um obstáculo desnecessário e, ocasionalmente, leva a erros muito perigosos.