2009-06-15 9 views
24

En C++, si se define esta función en header.hppVariable estática en el interior función de plantilla

void incAndShow() 
{ 
    static int myStaticVar = 0; 
    std::cout << ++myStaticVar << " " << std::endl; 
} 

y se incluye header.hpp en al menos dos archivos .cpp. Entonces tendrá multiple definition of incAndShow(). Que se espera Sin embargo, si se agrega una plantilla a la función

template <class T> 
void incAndShow() 
{ 
    static int myStaticVar = 0; 
    std::cout << ++myStaticVar << " " << std::endl; 
} 

entonces usted no tendrá ningún error multiple definition of. Del mismo modo, dos .cpp diferentes que llaman a la función con la misma plantilla (por ejemplo, incAndShow<int>()), compartirán myStaticVar. ¿Esto es normal? Estoy haciendo esta pregunta, porque confío en esta "característica" (compartiendo la variable estática) y quiero estar seguro de que no es solo mi implementación la que está haciendo esto.

+0

+1 Buena pregunta. Ahora estoy implementando algo que * "confíe en esta característica" *. – Nawaz

Respuesta

27

Puede confiar en esto. El ODR (Una definición de regla) dice en 3.2/5 en la Norma, donde D representa la plantilla de función no estático (tipo de letra cursiva por mí)

Si D es una plantilla, y se define en más de una traducción unidad, los últimos cuatro requisitos de la lista anterior se aplicarán a los nombres del ámbito adjunto de la plantilla utilizado en la definición de la plantilla (14.6.3), y también a los nombres dependientes en el momento de la creación de instancias (14.6.2).Si las definiciones de D satisfacen todos estos requisitos, entonces el programa se comportará como si hubiera una sola definición de D. Si las definiciones de D no satisfacen estos requisitos, entonces el comportamiento no está definido.

De los últimos cuatro requisitos, los dos más importantes son aproximadamente

  • cada definición de D consistirá en la misma secuencia de tokens
  • nombres en cada definición se refieren a las mismas cosas ("entidades")

Editar

Me imagino que esto por sí solo no es suficiente para garantizar que sus variables estáticas en las diferentes instancias sean todas iguales. Lo anterior solo garantiza que las múltiples definiciones de la plantilla son válidas. No dice algo sobre las especializaciones generadas a partir de él.

Esto es donde vinculación patadas en. Si el nombre de una especialización de plantilla de función (que es una función) tiene enlazado externo (3.5/4), entonces un nombre que se refiere a una especialización, se refiere a la misma función. Para una plantilla declarada estática, las funciones creadas a partir de ella tienen una vinculación interna, debido a

Las entidades generadas a partir de una plantilla con vinculación interna son distintas de todas las entidades generadas en otras unidades de traducción. -- 14/4

Un nombre que tiene alcance de espacio de nombres (3.3.6) tiene enlace interno si es el nombre de [...] un objeto, de referencia, función o plantilla de función que es explícitamente declararon estática -- 3.5/3

Si la plantilla de función no se declaró con estática, entonces tiene una vinculación externa (que, dicho sea de paso, también es la razón por la que tenemos que seguir la ODR en absoluto. De lo contrario, ¡D no se definiría de forma múltiple!). Esto se puede derivar de 14/4 (junto con 3.5/3)

una plantilla de función no miembro puede tener enlace interno; cualquier otro nombre de plantilla tendrá un enlace externo. -- 14/4.

Por último, llegamos a la conclusión de que una especialización de plantilla de función genera a partir de una plantilla de función con enlace externo en sí tiene enlazado externo por 3.5/4:

Un nombre que tenga una cobertura de espacio de nombres tiene enlazado externo si es el nombre de [...] una función, a menos que tenga vinculación interna -- 3.5/4

Y cuando se ha enlazado interno se explica por 3.5/3 para funciones proporcionada por explícita especializaciones, y 14/4 para especializaciones generadas (instancias de plantilla). Como el nombre de su plantilla tiene enlaces externos, todas sus especializaciones tienen enlaces externos: si utiliza su nombre (incAndShow<T>) desde diferentes unidades de traducción, se referirán a las mismas funciones, lo que significa que sus objetos estáticos serán los mismos en cada ocasión.

5

Solo para que entienda su pregunta. Se pregunta si es normal que cada versión de la función de plantilla tenga su propia instancia de myStaticVar. (Por ejemplo: incAndShow<int> vs. intAndShow<float> La respuesta es sí.

Su otra pregunta es, si dos archivos incluyen el encabezado que contiene la función de plantilla, ¿seguirán compartiendo la variable estática para una determinada T. Yo diría que sí.

0

Las plantillas se crean instancias según sea necesario, lo que significa que el compilador (¿vinculador también en este caso?) Se asegurará de no terminar con varias instancias de la misma plantilla, sino solo aquellas instancias de plantillas que usted Necesidad: en su caso, solo se instancia el incAndShow<int>() y nada más (de lo contrario, el compilador tendría que intentar crear instancias para cada tipo que no tenga sentido).

Así que supongo que los mismos métodos que utiliza para averiguar para qué tipo de instanciar la plantilla le impide crear dos instancias para el mismo tipo, p. solo una instancia de incAndShow<int>()

Esto es diferente de un código no de plantilla.

-3
  • sólo plantillas realmente serán convertidos en código una vez que se crea una instancia (es decir, usados)
  • cabeceras no se van a utilizar para el código de la aplicación, pero sólo para las declaraciones
+0

amigos, si se molestan en rechazarme, agradecería dejar un comentario sobre cuál es el problema, obviamente la pregunta original era bastante vaga ... – none

+2

"los encabezados no se deben usar para el código de implementación, pero solo para las declaraciones " No es cierto para las plantillas. Necesita definir ("implementar") plantillas en el encabezado. la exportación fue una forma de evitar esto, que ha demostrado ser demasiado difícil y, en general, no se implementa. – Suma

0

sí, es "normal", pero cualquier cosa que trate de lograr con esta "característica" puede ser cuestionable. Intenta explicar por qué quieres usar la variable estática local, puede que tengamos una manera más clara de hacerlo.

La razón por la que esto es normal se debe a la forma en que se compilan y vinculan las funciones de la plantilla. Cada unidad de traducción (las dos .cpp en su caso) obtiene su propia copia de incAndShow y cuando el programa está enlazado, las dos incAndShow se fusionarán en una sola. Si declara su función normal en línea en el archivo de encabezado, obtendrá un efecto similar.

1

La diferencia al crear la plantilla de función es que tiene una vinculación externa. Se podrá acceder al mismo incAndShow desde todas las unidades de traducción.

Parafraseando desde C++ borrador de trabajo estándar N2798 (2008-10-04): 14 parte 4: una plantilla de función no miembro puede tener enlace interno, otros siempre tienen un enlace externo. 14.8 punto 2: cada especialización tendrá su propia copia de la variable estática.

Su plantilla de función debe tener un enlace externo a menos que lo declare en el espacio de nombre sin nombre o algo así. Entonces, para cada T que use con su plantilla de función, debería obtener una variable estática utilizada para procesar el programa. En otras palabras, está bien confiar en tener solo una variable estática en el programa para cada instanciación de la plantilla (una para T == int, una para T == corto, etc.).

Como un lado, esto puede conducir a situaciones extrañas si define incAndShow de manera diferente en diferentes unidades de traducción. Por ejemplo, si lo define para incrementar en un archivo y la disminución en otro archivo (sin especificar el enlace interno poniendo la función en el espacio de nombre sin nombre), ambos compartirán la misma función, que se elegirá de forma aleatoria en el momento de la compilación. (con g ++ depende del orden en que se proporcionan los archivos de objeto en la línea de comando).

-1

Tome este ejemplo que muestra el comportamiento es absolutamente espera:

#include <iostream> 

template <class T> class Some 
{ 
public: 
    static int stat; 
}; 

template<class T> 
int Some<T>::stat = 10; 

void main() 
{ 
    Some<int>::stat = 5; 
    std::cout << Some<int>::stat << std::endl; 
    std::cout << Some<char>::stat << std::endl; 
    std::cout << Some<float>::stat << std::endl; 
    std::cout << Some<long>::stat << std::endl; 
} 

Se obtiene: 5 10 10 10 10

Lo anterior muestra que el cambio en la variable estática es solamente para el tipo "int" y por lo tanto en su caso de que no vea ningún problema

+3

-1: este ejemplo demuestra un miembro de clase estático; la pregunta es preguntar sobre la variable estática dentro de una función. –

Cuestiones relacionadas