after c++ header const primitive-types

c++ - after - const function javascript



¿Es mejor eliminar "const" delante de los tipos "primitivos" utilizados como parámetros de función en el encabezado? (5)

Como muchas otras personas han respondido, desde una perspectiva API, las siguientes son todas equivalentes, y son iguales para la resolución de sobrecarga:

void foo( int ); void foo( const int );

Pero una mejor pregunta es si esto proporciona algún significado semántico al consumidor de esta API, o si proporciona algún cumplimiento de buenas conductas por parte de un desarrollador de la implementación.

Sin unas pautas de codificación del desarrollador bien definidas que lo definan expresamente, los argumentos escalares const no tienen un significado semántico obvio .

De un consumidor: const int no cambia su entrada. Todavía puede ser un literal, o puede ser de otra variable ( const o non- const )

De un desarrollador: const int impone una restricción en una copia local de una variable (en este caso, un argumento de función). Esto solo significa modificar el argumento, tomar otra copia de la variable y modificarla en su lugar.

Cuando se llama a una función que acepta un argumento por valor, se hace una copia de ese argumento en la pila para la función llamada. Esto le da a la función una copia local del argumento para todo su alcance que luego puede modificarse, usarse para cálculos, etc., sin afectar la entrada original pasada a la llamada. Efectivamente, esto proporciona un argumento de variable local de su entrada.

Al marcar el argumento como const , simplemente significa que esta copia no se puede modificar; pero no prohíbe al desarrollador copiarlo y hacer modificaciones a esta copia. Dado que se trataba de una copia desde el principio, no aplica todo lo que se necesita desde el interior de la implementación, y en última instancia, no hace mucha diferencia desde la perspectiva del consumidor.

Esto está en contraste con pasar por referencia , en donde una referencia a int& es semánticamente diferente de const int& . El primero es capaz de mutar su entrada; este último solo es capaz de observar la entrada (siempre que la implementación no const const_cast la const -ness de distancia) pero deja de lado esta posibilidad); por lo tanto, la const -ness en las referencias tiene un significado semántico implícito.

No proporciona muchos beneficios en la API pública; y (imo) introduce restricciones innecesarias en la implementación. Como un ejemplo arbitrario y artificial: una función simple como:

void do_n_times( int n ) { while( n-- > 0 ) { // do something n times } }

ahora tendría que escribirse usando una copia innecesaria:

void do_n_times( const int n ) { auto n_copy = n; while( n_copy-- > 0 ) { // do something n times } }

Independientemente de si los escalares const se utilizan en la API pública, una cosa clave es ser coherente con el diseño. Si la API cambia aleatoriamente entre el uso de argumentos escalares const para usar escalares no const , entonces puede causar confusión en cuanto a si debe haber algún significado implícito para el consumidor.

TL; DR: los tipos escalares const en una API pública no transmiten significado semántico a menos que se defina explícitamente en sus propias pautas para su dominio.

En el proceso de revisión del código, uno de mis compañeros de trabajo me mencionó que "const" s delante de "tipos primitivos" utilizados como parámetro de función en un encabezado no tiene sentido, y recomendó eliminar estos "const" s. Sugirió usar "const" solo en el archivo fuente en tales casos. Los tipos primitivos significan tipos como "int", "char", "float", etc.

El siguiente es un ejemplo.

ejemplo.h

int ProcessScore(const int score);

ejemplo.cc

int ProcessScore(const int score) { // Do some calculation using score return some_value; }

Su sugerencia es hacer lo siguiente:

ejemplo.h

int ProcessScore(int score); // const is removed here.

ejemplo.cc

int ProcessScore(const int score) { // Do some calculation using score return some_value; }

Pero estoy algo confundido. Por lo general, el usuario solo verá el encabezado, por lo que si hay inconsistencia entre el encabezado y el archivo de origen, podría causar confusión.

¿Alguien puede dar algún consejo sobre esto?


El parámetro de función declarado const y sin const es el mismo al llegar a una resolución de sobrecarga. Entonces, por ejemplo, funciones

void f(int); void f(const int);

son lo mismo y no podrían definirse juntos. Como resultado, es mejor no usar const en la declaración de parámetros para evitar posibles duplicaciones. (No estoy hablando de const reference o const pointer, ya que el modificador const no es de nivel superior).

Aquí hay una cita exacta del estándar.

Después de producir la lista de tipos de parámetros, cualquier cv-qualifiers de nivel superior que modifique un tipo de parámetro se elimina cuando se forma el tipo de función. La lista resultante de tipos de parámetros transformados y la presencia o ausencia de puntos suspensivos o un paquete de parámetros de funciones es la lista de tipos de parámetros de la función. [Nota: esta transformación no afecta los tipos de los parámetros. Por ejemplo, int(*)(const int p, decltype(p)*) e int(*)(int, const int*) son tipos idénticos. - nota final]

La utilidad de const en la definición de función es discutible -el razonamiento detrás de esto es lo mismo que usar const para declarar variable local- demuestra a otros programadores que leen el código que este valor no va a modificarse dentro de la función.


Para todos los tipos (no solo primitivos), se ignoran los calificadores const del nivel superior en la declaración de la función. Entonces los siguientes cuatro declaran la misma función:

void foo(int const i, int const j); void foo(int i, int const j); void foo(int const i, int j); void foo(int i, int j);

Sin embargo, el calificador const no se ignora dentro del cuerpo de la función. Ahí puede tener un impacto en la exactitud de const. Pero ese es un detalle de implementación de la función. Entonces el consenso general es este:

  1. Deja la infección fuera de la declaración. Es solo desorden, y no afecta cómo los clientes llamarán a la función.

  2. Deje la constante en la definición si desea que el compilador detecte cualquier modificación accidental del parámetro.


Siga las recomendaciones que le dieron en la revisión del código.

El uso de const para argumentos de valor no tiene ningún valor semántico, solo es significativo (potencialmente) para la implementación de su función, e incluso en ese caso yo diría que es innecesario.

editar: para que quede claro: el prototipo de su función es la interfaz pública para su función. Lo que hace es ofrecer una garantía de que no modificará las referencias.

int a = 7; do_something( a ); void do_something( int& x ); // ''a'' may be modified void do_something( const int& x ); // I will not modify ''a'' void do_something( int x ); // no one cares what happens to x

El uso de const es algo similar a TMI; no es importante en ninguna parte excepto dentro de la función si se modifica o no ''x''.

edit2: también me gusta mucho la información en la respuesta de StoryTeller


Pensé que const es una pista para el compilador de que algunas expresiones no cambian y para optimizar en consecuencia. Por ejemplo, estaba probando si un número es primordial buscando divisores hasta la raíz cuadrada del número y pensé que declarar el argumento const tomaría el sqrt(n) fuera del ciclo for , pero no fue así.

Puede que no sea necesario en el encabezado, pero, de nuevo, podría decir que todo lo que necesita es no modificar el argumento y nunca es necesario. Prefiero ver const donde está const, no solo en la fuente, sino también en el encabezado. Las inconsistencias entre la declaración y la definición me vuelven circunspecto. Solo es mi opinión.