c++ - variable - static function
variables estáticas en una función en línea (9)
Además de cualquier problema de diseño, todo esto puede implicar, ya que ya está atrapado con él, debe utilizar estática en este caso, no en línea. De esa forma, todos comparten las mismas variables. (Función estática)
Tengo una función que se declara y define en un archivo de encabezado. Este es un problema en sí mismo. Cuando esa función no está en línea, cada unidad de traducción que usa ese encabezado obtiene una copia de la función, y cuando están vinculadas entre sí, se duplican. Lo "corregí" al hacer la función en línea, pero me temo que esta es una solución frágil porque, hasta donde yo sé, el compilador no garantiza la alineación, incluso cuando se especifica la palabra clave "en línea". Si esto no es verdad, por favor corrígeme.
De todos modos, la verdadera pregunta es, ¿qué sucede con las variables estáticas dentro de esta función? ¿Con cuántas copias llego?
Creo que el compilador crea muchas copias de la variable, pero el enlazador elige una y hace que todas las demás la hagan referencia. Obtuve resultados similares cuando probé un experimento para crear diferentes versiones de una función en línea; si la función no estaba en línea (modo de depuración), todas las llamadas pasaron a la misma función, independientemente del archivo de origen desde el que se llamaron.
Piensa como un compilador por un momento, ¿cómo podría ser de otra manera? Cada unidad de compilación (archivo fuente) es independiente de las demás y se puede compilar por separado; cada uno debe, por lo tanto, crear una copia de la variable, creyendo que es la única. El enlazador tiene la capacidad de alcanzar esos límites y ajustar las referencias para variables y funciones.
Creo que terminarás con uno por unidad de traducción. De hecho, tiene muchas versiones de esa función (y su variable estática declarada), una para cada unidad de traducción que incluye el encabezado.
Desde que escribí la pregunta, la probé con Visual Studio 2008. Intenté activar todas las opciones que hacen que VS actúe de acuerdo con las normas, pero es posible que haya omitido algunas. Estos son los resultados:
Cuando la función es meramente "en línea", solo hay una copia de la variable estática.
Cuando la función es "estática en línea", hay tantas copias como unidades de traducción.
La verdadera pregunta ahora es si se supone que las cosas son así, o si se trata de una ideosincracia del compilador de Microsoft C ++.
Encontré útil la respuesta de Mark Ransom: el compilador crea muchas copias de la variable estática, pero el vinculador elige una y la aplica en todas las unidades de traducción.
En otro lugar encontré esto:
Ver [dcl.fct.spec] / 4
[..] Una función en línea con enlace externo tendrá la misma dirección en todas las unidades de traducción. Una variable local estática en una función externa en línea siempre se refiere al mismo objeto. Un literal de cadena en una función en línea externa es el mismo objeto en diferentes unidades de traducción.
No tengo una copia del estándar para verificar, pero coincide con mi experiencia al examinar el ensamblaje en VS Express 2008
Estático significa que una copia se distribuye por todo el programa, pero en línea significa que requiere el mismo código por varias veces en el mismo programa, por lo que no es posible hacer una variable estática dentro de la función en línea.
Inline significa que el código ejecutable (instrucciones) está incluido en el código de la función de llamada. El compilador puede elegir hacer eso independientemente de si lo solicitó. Eso no tiene ningún efecto sobre las variables (datos) declaradas en la función.
Se supone que es de esta manera. "estático" le dice al compilador que desea que la función sea local para la unidad de compilación, por lo tanto, quiere una copia por unidad de compilación y una copia de las variables estáticas por instancia de la función.
"en línea" solía decirle al compilador que desea que la función esté en línea; hoy en día, simplemente lo toma como "está bien si hay varias copias del código, solo asegúrese de que sea la misma función". Entonces todos comparten las variables estáticas.
Nota: esta respuesta se escribió en respuesta a la respuesta que el póster original publicó en su cuenta.
Supongo que te estás perdiendo algo, aquí.
función estática?
Declarar una función estática lo hará "oculto" en su unidad de compilación.
Un nombre que tiene un ámbito de espacio de nombres (3.3.6) tiene un enlace interno si es el nombre de
- una variable, función o plantilla de función que se declara explícitamente estática;
3.5 / 3 - C ++ 14 (n3797)
Cuando un nombre tiene un enlace interno, la entidad a la que denota se puede referir con nombres de otros ámbitos en la misma unidad de traducción.
3.5 / 2 - C ++ 14 (n3797)
Si declara esta función estática en un encabezado, todas las unidades de compilación, incluido este encabezado, tendrán su propia copia de la función.
El problema es que si hay variables estáticas dentro de esa función, cada unidad de compilación que incluya este encabezado también tendrá su propia versión personal.
función en línea?
Al declararlo en línea, es un candidato para la alineación (no significa mucho en la actualidad en C ++, ya que el compilador se alineará o no, a veces ignorando el hecho de que la palabra clave en línea está presente o ausente):
Una declaración de función (8.3.5, 9.3, 11.3) con un especificador en línea declara una función en línea. El especificador en línea indica a la implementación que la sustitución en línea del cuerpo de la función en el punto de llamada es preferible al mecanismo de llamada de función habitual. No se requiere una implementación para realizar esta sustitución en línea en el punto de llamada; sin embargo, incluso si esta sustitución en línea se omite, las otras reglas para las funciones en línea definidas en 7.1.2 se seguirán respetando.
7.1.2 / 2 - C ++ 14 (n3797)
En un encabezado, tiene un efecto secundario interesante: la función en línea se puede definir varias veces en el mismo módulo, y el vinculador simplemente unirá "ellos" en uno (si no fueron incluidos por razones del compilador).
Para las variables estáticas declaradas en el interior, el estándar dice específicamente que hay una, y solo una de ellas:
Una variable local estática en una función externa en línea siempre se refiere al mismo objeto.
7.1.2 / 4 - C ++ 98 / C ++ 14 (n3797)
(las funciones son por defecto externas, entonces, a menos que específicamente marque su función como estática, esto se aplica a esa función)
Esto tiene la ventaja de "estático" (es decir, se puede definir en un encabezado) sin sus defectos (existe a lo sumo una vez si no está en línea).
variable local estática?
Las variables locales estáticas no tienen ningún vínculo (no pueden mencionarse por su nombre fuera de su alcance), pero tienen una duración de almacenamiento estático (es decir, es global, pero su construcción y destrucción obedecen a reglas específicas).
estática + en línea?
Mezclar en línea y estático tendrá las consecuencias que usted describió (incluso si la función está en línea, la variable estática interna no lo estará, y terminará con tantas variables estáticas como unidades de compilación, incluida la definición de sus funciones estáticas )
Responda a la pregunta adicional del autor
Desde que escribí la pregunta, la probé con Visual Studio 2008. Intenté activar todas las opciones que hacen que VS actúe de acuerdo con las normas, pero es posible que haya omitido algunas. Estos son los resultados:
Cuando la función es meramente "en línea", solo hay una copia de la variable estática.
Cuando la función es "estática en línea", hay tantas copias como unidades de traducción.
La verdadera pregunta ahora es si se supone que las cosas son así, o si esta es una idiosincrasia del compilador de Microsoft C ++.
Entonces supongo que tienes algo así:
void doSomething()
{
static int value ;
}
Debes darte cuenta de que la variable estática dentro de la función, simplemente pone, una variable global oculta a todos menos al alcance de la función, lo que significa que solo la función declarada en su interior puede alcanzarla.
Alinear la función no cambiará nada:
inline void doSomething()
{
static int value ;
}
Solo habrá una variable global oculta. El hecho de que el compilador intente alinear el código no cambiará el hecho de que solo hay una variable oculta global.
Ahora, si su función se declara estática:
static void doSomething()
{
static int value ;
}
Entonces es "privado" para cada unidad de compilación, lo que significa que cada archivo CPP que incluye el encabezado donde se declara la función estática tendrá su propia copia privada de la función, incluida su propia copia privada de la variable oculta global, por lo tanto tantas variables como hay unidades de compilación que incluyen el encabezado.
Agregar "en línea" a una función "estática" con una variable "estática" dentro:
inline static void doSomething()
{
static int value ;
}
tiene el mismo resultado que no agregar esta palabra clave "en línea", en lo que se refiere a la variable estática interna.
Entonces, el comportamiento de VC ++ es correcto, y está confundiendo el significado real de "en línea" y "estático".