C ++ vinculación y especializaciones de plantillas

Estoy estudiando el comportamiento del enlazador de C ++ con respecto a las especializaciones de plantillas. Estoy usando Microsoft Visual C ++ 2010 para estos experimentos. No sé si el comportamiento es el mismo con otras cadenas de herramientas (por ejemplo, gcc).

Aquí hay un primer fragmento de código:

// bar.cpp template  int foo() { return 1; } int bar() { return foo(); } // main.cpp template  int foo() { return 1; } template  int foo() { return 2; } int bar(); int main() { const int x = bar(); const int y = foo(); // doesn't link } 

Como era de esperar, este código no se vincula porque foo() tiene varias definiciones, ya que se crea una instancia en bar.cpp y una vez en main.cpp (a través de la especialización). Entonces, si este progtwig se vinculara , esperaríamos que la bar() y main() usaran distintas instancias de foo() manera que al final tendríamos x == 1 y y == 2.

Corrigamos el error de enlace declarando la especialización de foo() como static :

 // bar.cpp template  int foo() { return 1; } int bar() { return foo(); } // main.cpp template  int foo() { return 1; } template  static int foo() { return 2; } // note: static int bar(); int main() { const int x = bar(); // x == 1 const int y = foo(); // y == 2 } 

Ahora tenemos x == 1 y y == 2, como esperábamos. (Nota: debemos usar la palabra clave static aquí: un espacio de nombres anónimo no funcionará ya que no podemos especializar una función de plantilla en un espacio de nombres diferente al de su statement).

Ahora, el uso de la palabra clave static es bastante poco intuitivo. Normalmente, la especialización foo() residiría en algún lugar de un archivo de encabezado y, por lo tanto, se marcaría como en línea, como en el siguiente fragmento de código:

 // bar.cpp template  int foo() { return 1; } int bar() { return foo(); } // main.cpp template  int foo() { return 1; } template  inline int foo() { return 2; } // note: inline int bar(); int main() { const int x = bar(); // x == 2 const int y = foo(); // y == 2 } 

Este código ahora se enlaza correctamente y cuando lo ejecutamos obtenemos x == 2 e y == 2. Este es el bit que me parece sorprendente: ¿por qué hay una sola definición de foo() ? ¿Cuál es el significado de en inline en este código?

Un último fragmento:

 // bar.cpp template  int foo() { return 1; } int bar() { return foo(); } // main.cpp template  int foo() { return 1; } template  inline int foo() { return 2; } // note: inline int bar(); int main() { const int x = bar(); // x == 1 // const int y = foo(); // note: commented out } 

Este caso en realidad no es sorprendente: la especialización de foo() ya no está instanciada en main.cpp (aunque la statement aún está ahí), por lo que la única instanciación restante es la de bar.cpp .

En realidad estás violando una regla de C ++ aquí (énfasis mío):

14.7.3 [temp.expl.spec] :

6 / Si una plantilla, una plantilla miembro o un miembro de una plantilla de clase está explícitamente especializada, dicha especialización se declarará antes del primer uso de esa especialización que causaría una creación de instancias implícita, en cada unidad de traducción en la que dicha el uso ocurre no se requiere diagnóstico Si el progtwig no proporciona una definición para una especialización explícita y la especialización se usa de una manera que podría causar una instanciación implícita o si el miembro es una función miembro virtual, el progtwig no está configurado correctamente, no se requiere un diagnóstico . Nunca se genera una creación de instancias implícita para una especialización explícita que se declara pero no se define. [ Ejemplo :

 class String { }; template class Array { /* ... */ }; template void sort(Array& v) { /* ... */ } void f(Array& v) { sort(v); // use primary template // sort(Array&), T is String } template<> void sort(Array& v); // error: specialization // after use of primary template template<> void sort<>(Array& v); // OK: sort not yet used template struct A { enum E : T; enum class S : T; }; template<> enum A::E : int { eint }; // OK template<> enum class A::S : int { sint }; // OK template enum A::E : T { eT }; template enum class A::S : T { sT }; template<> enum A::E : int { echar }; // ill-formed, // A::E was instantiated // when A was instantiated template<> enum class A::S : int { schar }; // OK 

ejemplo final ]