Los const superfluos adicionales son malos desde un punto de vista API:
Poner constantes superfluos adicionales en su código para los parámetros de tipo intrínsecos pasados por el valor satura su API sin hacer ninguna promesa significativa al llamante o al usuario de la API (solo obstaculiza la implementación).
Demasiados 'const' en una API cuando no son necesarios es como " lobo llorón ", eventualmente la gente comenzará a ignorar 'const' porque está por todas partes y no significa nada la mayor parte del tiempo.
El argumento "reductio ad absurdum" para consts adicionales en API es bueno para estos dos primeros puntos, si más parámetros de const son buenos, entonces cada argumento que pueda tener una const, DEBE tener una const. De hecho, si fuera realmente tan bueno, desearía que const sea el valor predeterminado para los parámetros y tenga una palabra clave como "mutable" solo cuando desee cambiar el parámetro.
Así que intentemos poner en constante donde podamos:
void mungerum(char * buffer, const char * mask, int count);
void mungerum(char * const buffer, const char * const mask, const int count);
Considere la línea de código anterior. La declaración no solo es más abarrotada y más larga y más difícil de leer, sino que tres de las cuatro palabras clave 'const' pueden ser ignoradas de manera segura por el usuario API. Sin embargo, el uso adicional de 'const' ha hecho que la segunda línea sea potencialmente PELIGROSA.
¿Por qué?
Una lectura errónea rápida del primer parámetro char * const buffer
podría hacerle pensar que no modificará la memoria en el búfer de datos que se pasa; sin embargo, ¡esto no es cierto! El 'const' superfluo puede conducir a suposiciones peligrosas e incorrectas sobre su API cuando se escanea o lee mal rápidamente.
Las constantes superfluas también son malas desde el punto de vista de implementación 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);
Si FLEXIBLE_IMPLEMENTATION no es cierto, entonces la API es "prometedora" de no implementar la función de la primera manera a continuación.
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];
}
}
Esa es una promesa muy tonta de hacer. ¿Por qué debería hacer una promesa que no brinda ningún beneficio a su interlocutor y solo limita su implementación?
Sin embargo, ambas son implementaciones perfectamente válidas de la misma función, por lo que todo lo que has hecho es atar una mano a la espalda innecesariamente.
Además, es una promesa muy superficial que es fácil (y legalmente eludida).
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);
}
Mire, lo implementé de esa manera de todos modos, aunque prometí no hacerlo, solo usando una función de contenedor. Es como cuando el chico malo promete no matar a alguien en una película y le ordena a su secuaz que lo mate.
Esas constantes superfluas no valen más que una promesa de un chico malo de la película.
Pero la capacidad de mentir es aún peor:
Me han aclarado que puede no coincidir const en el encabezado (declaración) y el código (definición) mediante el uso de const espuria. Los defensores de const-happy afirman que esto es algo bueno, ya que le permite poner const solo en la definición.
// Example of const only in definition, not declaration
class foo { void test(int *pi); };
void foo::test(int * const pi) { }
Sin embargo, lo contrario es cierto ... puede poner una constante espuria solo en la declaración e ignorarla en la definición. Esto solo hace que la constante superflua en una API sea más una cosa terrible y una mentira horrible: vea este ejemplo:
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
}
Todo lo que hace const superfluo en realidad es hacer que el código del implementador sea menos legible al obligarlo a usar otra copia local o una función de envoltura cuando quiere cambiar la variable o pasar la variable por referencia no const.
Mira este ejemplo. ¿Cuál es más legible? ¿Es obvio que la única razón para la variable adicional en la segunda función es porque algún diseñador de API arrojó una constante 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;
}
}
Ojalá hayamos aprendido algo aquí. Const superfluo es una monstruosidad atormentada API, un fastidio molesto, una promesa superficial y sin sentido, un obstáculo innecesario, y ocasionalmente conduce a errores muy peligrosos.