Variable estática de plantilla

No puedo entender por qué si definimos la variable estática de la clase habitual (sin plantilla) en el encabezado, tenemos un error de enlazador, pero en el caso de las plantillas, todo funciona bien y, además, tendremos una única instancia de variable estática entre todas las unidades de traducción :

Es el encabezado de la plantilla (template.h):

// template.h template class Templ { public: static int templStatic; }; template Templ::templStatic = 0; 

Es la primera unidad que usa la plantilla (unit1.cpp)

 // unit1.cpp #include "template.h" int method1() { return Templ::templStatic++; } 

Segunda unidad aquí (unit2.cpp):

 // unit2.cpp #include "template.h" int method2() { return Templ::templStatic++; } 

Y, finalmente, main.cpp:

 // main.cpp #include  int method1(); int method2(); int main(int argc, char** argv) { std::cout << method1() << std::endl; std::cout << method2() << std::endl; } 

Después de comstackr, vincular y ejecutar este código, tendremos el siguiente resultado:

 0 1 

Entonces, ¿por qué en el caso de las plantillas todo funciona bien (y como se espera)? ¿Cómo manejan esto el comstackdor o el enlazador (podemos comstackr cada archivo .cpp en una llamada separada del comstackdor, y luego vincularlos con caling to linker, para que el comstackdor y el enlazador no “vean” todos los archivos .cpp al mismo tiempo)?

PD: mi comstackdor: msvcpp 9 (pero también lo verificó en mingw)

Es porque la definición del miembro de datos estáticos es en sí misma una plantilla. Permitir esto es necesario por el mismo motivo por el que se le permite tener una plantilla de función que no está en línea varias veces en un progtwig. Necesita la plantilla para generar la entidad resultante (por ejemplo, una función o un miembro de datos estáticos). Si no se le permitiera poner la definición de un miembro de datos estático, ¿cómo crearía la siguiente instancia?

 template struct F { static int const value; }; template int const F::value = sizeof(T); 

No se sabe qué es T : el Estándar dice que la definición fuera de la plantilla de clase es una definición de plantilla, en la que los parámetros se heredan de su propietario de plantilla de clase.


He hecho algunos experimentos con GCC. A continuación, tenemos una instanciación implícita de F::value , y una especialización explícita de F::value que debe definirse en un archivo .cpp para no causar errores de símbolos duplicados cuando se incluyen varias veces .

 // Translation Unit 1 template struct F { static int value; }; template int F::value = sizeof(T); // this would belong into a .cpp file template<> int F::value = 2; // this implicitly instantiates F::value int test = F::value; int main() { } 

La segunda unidad de traducción contiene solo otra instanciación implícita del mismo miembro de datos estáticos

 template struct F { static int value; }; template int F::value = sizeof(T); int test1 = F::value; 

Esto es lo que obtenemos con GCC: convierte cada instancia implícita en un símbolo débil y lo pega en su propia sección aquí. Los símbolos débiles no causarán errores cuando existan múltiples en el momento del enlace. En su lugar, el vinculador elegirá una instancia y descarta las otras suponiendo que todas son iguales

 objdump -Ct main1.o # => # cut down to the important ones 00000000 l df *ABS* 00000000 main1.cpp 0000000a l F .text 0000001e __static_initialization_and_destruction_0(int, int) 00000000 ld .data._ZN1FIfE5valueE 00000000 .data._ZN1FIfE5valueE 00000028 l F .text 0000001c global constructors keyed to _ZN1FIcE5valueE 00000000 g O .data 00000004 F::value 00000000 g O .bss 00000004 test 00000000 g F .text 0000000a main 00000000 w O .data._ZN1FIfE5valueE 00000004 F::value 

Entonces, como podemos ver, F::value es un símbolo débil, lo que significa que el enlazador puede ver varios de estos en el momento del enlace. test , main y F::value son símbolos globales (no débiles). Al unir main1.o y main2.o , vemos en el mapa salida ( -Wl,-M ) lo siguiente

 # (mangled name) .data._ZN1FIfE5valueE 0x080497ac 0x4 main1.o 0x080497ac F::value 

Esto indica que en realidad cae todo, excepto una instancia.

Hay una solución, puedes crear una clase padre y poner la variable estática en ella, luego hacer que tu clase de plantilla la herede de forma privada, he aquí un ejemplo:

 class Parent { protected: static long count; }; long Parent::count = 0; template class TemplateClass: private Parent { private: int mKey; public: TemplateClass():mKey(count++){} long getKey(){return mKey;} } int main() { TemplateClass obj1; TemplateClass obj2; std::cout<<"Object 1 key is: "< 

La salida será:

 Object 1 key is: 0 Object 2 key is: 1