¿Const-correctness le da al comstackdor más espacio para la optimización?

Sé que mejora la legibilidad y hace que el progtwig sea menos propenso a errores, pero ¿cuánto mejora el rendimiento?

Y en una nota al margen, ¿cuál es la principal diferencia entre una referencia y un puntero const ? Asumiría que están almacenados en la memoria de manera diferente, pero ¿cómo?

[Editar: OK, así que esta pregunta es más sutil de lo que pensaba al principio.]

Declarar un puntero-a-const o referencia-de-const nunca ayuda a ningún comstackdor a optimizar nada. (Aunque vea la Actualización al final de esta respuesta).

La statement const solo indica cómo se usará un identificador dentro del scope de su statement; no dice que el objeto subyacente no puede cambiar.

Ejemplo:

 int foo(const int *p) { int x = *p; bar(x); x = *p; return x; } 

El comstackdor no puede suponer que *p no se modifique mediante la llamada a bar() , porque p podría ser (por ejemplo) un puntero a un int global y la bar() podría modificarlo.

Si el comstackdor sabe lo suficiente acerca de la persona que llama de foo() y el contenido de la bar() que puede probar que bar() no modifica *p , entonces también puede realizar esa prueba sin la statement const .

Pero esto es cierto en general. Como const solo tiene un efecto dentro del scope de la statement, el comstackdor ya puede ver cómo está tratando el puntero o referencia dentro de ese scope; ya sabe que no está modificando el objeto subyacente.

En resumen, todo lo que const hace en este contexto es evitar cometer errores. No le dice al comstackdor nada que no sepa, y por lo tanto es irrelevante para la optimización.

¿Qué pasa con las funciones que llaman foo() ? Me gusta:

 int x = 37; foo(&x); printf("%d\n", x); 

¿Puede el comstackdor probar que esto imprime 37, ya que foo() toma una const int * ?

No. Aunque foo() toma un puntero-a-const, puede descartar la constidad y modificar el int. (Esto no es un comportamiento indefinido.) Aquí nuevamente, el comstackdor no puede hacer suposiciones en general; y si sabe lo suficiente sobre foo() para realizar dicha optimización, sabrá que incluso sin la const .

La única vez que const podría permitir optimizaciones es en casos como este:

 const int x = 37; foo(&x); printf("%d\n", x); 

Aquí, modificar x través de cualquier mecanismo (p. Ej., Tomando un puntero y descartando la const ) es invocar Comportamiento Indefinido. Por lo tanto, el comstackdor puede suponer que no lo hace y puede propagar la constante 37 en printf (). Este tipo de optimización es legal para cualquier objeto que declare const . (En la práctica, una variable local a la que nunca toma una referencia no se beneficiará, porque el comstackdor ya puede ver si la modifica dentro de su scope).

Para responder a su pregunta de “nota lateral”, (a) un puntero const es un puntero; y (b) un puntero const puede ser NULL. Tiene razón en que la representación interna (es decir, una dirección) probablemente sea la misma.

[actualizar]

Como señala Christoph en los comentarios, mi respuesta es incompleta porque no menciona la restrict .

La sección 6.7.3.1 (4) de la norma C99 dice:

Durante cada ejecución de B, sea L cualquier valor l que tenga & L basado en P. Si L se usa para acceder al valor del objeto X que designa, y X también se modifica (por cualquier medio), entonces se aplican los siguientes requisitos : T no debe ser const-calificado. …

(Aquí B es un bloque básico sobre el cual P, un restrictivo-puntero-a-T, está dentro del scope).

Entonces, si una función C foo() se declara así:

 foo(const int * restrict p) 

… entonces el comstackdor puede suponer que no ocurren modificaciones a *p durante la vida de p – es decir, durante la ejecución de foo() – porque de lo contrario el Comportamiento sería Indefinido.

Por lo tanto, en principio, combinar restrict con un puntero-a-const podría habilitar las dos optimizaciones que se descartan anteriormente. ¿Realmente algún comstackdor implementa tal optimización, me pregunto? (GCC 4.5.2, al menos, no lo hace)

Tenga en cuenta que restrict solo existe en C, no en C ++ (ni siquiera en C ++ 0x), excepto como una extensión específica del comstackdor.

Por encima de todo, puedo pensar en dos casos en los que la calificación de const adecuada permite optimizaciones adicionales (en los casos en que el análisis de todo el progtwig no está disponible):

 const int foo = 42; bar(&foo); printf("%i", foo); 

Aquí, el comstackdor sabe imprimir 42 sin tener que examinar el cuerpo de la bar() (que podría no ser visible en la unidad de traducción) porque todas las modificaciones a foo son ilegales ( esto es lo mismo que el ejemplo de Nemo ).

Sin embargo, esto también es posible sin marcar foo como const al declarar bar() como

 extern void bar(const int *restrict p); 

En muchos casos, el progtwigdor en realidad quiere restrict punteros calificados para const y no punteros claros para const como parámetros de función, ya que solo el primero da garantías sobre la mutabilidad de los objetos apuntados.

En cuanto a la segunda parte de su pregunta: Para todos los propósitos prácticos, una referencia de C ++ puede considerarse como un puntero constante (¡no como un puntero a un valor constante!) Con indirección automática: no es “más seguro” o “más rápido” que un puntero, simplemente más conveniente.

Hay dos problemas con const en C ++ (en lo que respecta a la optimización):

  • const_cast
  • mutable

const_cast significa que aunque pase un objeto por const reference o const pointer, la función puede alejar la constidad y modificar el objeto (permitido si el objeto no es const para comenzar).

mutable significa que aunque un objeto es const , algunas de sus partes pueden ser modificadas (comportamiento de caché). Además, los objetos señalados (en lugar de ser propiedad) se pueden modificar en métodos const , incluso cuando lógicamente son parte del estado del objeto. Y, finalmente, las variables globales también se pueden modificar …

const está aquí para ayudar al desarrollador a detectar errores lógicos temprano.

const-correctness generalmente no ayuda al rendimiento; la mayoría de los comstackdores ni siquiera se molestan en seguir la constness más allá de la interfaz. Marcar variables como const puede ayudar, dependiendo de la situación.

Las referencias y punteros se almacenan exactamente de la misma manera en la memoria.

Esto realmente depende del comstackdor / plataforma (puede ayudar a la optimización en algún comstackdor que aún no se haya escrito, o en alguna plataforma que nunca use). Los estándares C y C ++ no dicen nada sobre el rendimiento, aparte de dar requisitos de complejidad para algunas funciones.

Los punteros y las referencias a const generalmente no ayudan a la optimización, ya que la calificación const puede eliminarse legalmente en algunas situaciones, y es posible que el objeto pueda ser modificado por una referencia no const diferente. Por otro lado, declarar que un objeto es const puede ser útil, ya que garantiza que el objeto no se puede modificar (incluso cuando se pasa a funciones que el comstackdor desconoce). Esto permite que el comstackdor almacene el objeto const en la memoria de solo lectura, o almacene su valor en un lugar centralizado, lo que reduce la necesidad de copias y verifica modificaciones.

Los punteros y las referencias generalmente se implementan de la misma manera, pero una vez más esto depende totalmente de la plataforma. Si está realmente interesado, debería mirar el código máquina generado para su plataforma y comstackdor en su progtwig (si es que está usando un comstackdor que genera código de máquina).

Una cosa es que si declaras una constante de variable global, es posible ponerla en la parte de solo lectura de una biblioteca o ejecutable y así compartirla entre múltiples procesos con un mmap de solo lectura. Esto puede ser una gran ganancia de memoria en Linux al menos si tiene una gran cantidad de datos declarados en variables globales.

Otra situación, si declara un entero global constante, o float o enum, el comstackdor puede simplemente poner la constante en línea en lugar de usar una referencia de variable. Eso es un poco más rápido, aunque creo que no soy un experto en comstackdores.

Las referencias son solo indicadores debajo de implementación.

Puede ayudar un poco al rendimiento, pero solo si está accediendo al objeto directamente a través de su statement. Los parámetros de referencia y demás no se pueden optimizar, ya que puede haber otras rutas a un objeto que no se haya declarado originalmente const, y el comstackdor generalmente no puede decir si el objeto al que se hace referencia se declaró realmente const o no a menos que esa sea la statement que está utilizando.

Si está utilizando una statement const, el comstackdor sabrá que los cuerpos de funciones comstackdos externamente, etc. no pueden modificarlo, por lo que obtiene un beneficio allí. Y, por supuesto, cosas como const int se propagan en tiempo de comstackción, por lo que es una gran victoria (en comparación con solo un int).

Las referencias y los punteros se almacenan exactamente igual, simplemente se comportan de forma sintáctica diferente. Las referencias son básicamente renombrados, por lo que son relativamente seguros, mientras que los punteros pueden señalar muchas cosas diferentes y, por lo tanto, son más potentes y propensos a errores.

Supongo que el puntero const sería arquitectónicamente idéntico a la referencia, por lo que el código de máquina y la eficiencia serían los mismos. la diferencia real es la syntax: las referencias son una syntax más limpia y fácil de leer, y como no se necesita la maquinaria adicional proporcionada por un puntero, una referencia sería estilísticamente preferida.