2010-04-29 6 views
10

Vea el siguiente código y aclare mis dudas.¿Cómo afectan las instancias de plantilla explícitas a lo que el enlazador puede encontrar?

  1. como el ABC es una plantilla, ¿por qué no muestra un error cuando ponemos la definición de la función miembro de la clase ABC en test.cpp?

  2. Si pongo el código test.cpp en test.h y remve 2, funciona bien. ¿Por qué?

.

// test.h 
template <typename T> 
class ABC { 
public: 
    void foo(T&); 
    void bar(T&); 
}; 
// test.cpp 
template <typename T> 
void ABC<T>::foo(T&) {} // definition 
template <typename T> 
void ABC<T>::bar(T&) {} // definition 

template void ABC<char>::foo(char &); // 1 
template class ABC<char>;    // 2 
// main.cpp 
#include "test.h" 
int main() { 
    ABC<char> a; 
    a.foo();  // valid with 1 or 2 
    a.bar();  // link error if only 1, valid with 2 
} 
+0

¿Por qué lo preguntas? Son dos declaraciones bastante diferentes. –

+0

@Dennis Zickefoose: todos eran principiantes en algún momento – ereOn

+0

@ereOn: Sí, pero ¿por qué le está pidiendo puede recorrer un largo camino hacia lo que sugiere un enfoque adecuado para tomar al responder a la pregunta. –

Respuesta

13

En ambos casos, está realizando una instanciación explícita. En el segundo caso, solo se está creando una instancia de ABC<char>::foo, mientras que en el primer caso también se está instanciando ABC<char>::bar.

A diferente ejemplo similar puede aclarar las implicaciones:

// test.h 
template <typename T> 
class ABC { 
public: 
    void foo(T&); 
    void bar(T&); 
}; 
// test.cpp 
template <typename T> 
void ABC<T>::foo(T&) {} // definition 
template <typename T> 
void ABC<T>::bar(T&) {} // definition 

template void ABC<char>::foo(char &); // 1 
template class ABC<char>;    // 2 
// main.cpp 
#include "test.h" 
int main() { 
    ABC<char> a; 
    a.foo();  // valid with 1 or 2 
    a.bar();  // link error if only 1, valid with 2 
} 

En el ejemplo, en main el compilador no puede ver foo ni bar definiciones, lo que no puede instanciar los métodos. El compilador, al procesar main.cpp aceptará el código en main con mucho gusto, ya que le está diciendo que ABC es una plantilla y que tiene esas dos funciones, y asumirá que se definirán en alguna otra unidad de traducción.

En la unidad de traducción que contiene test.cpp el compilador está viendo ambas definiciones de métodos, y las dos instancias (método/clase) se pueden procesar por completo. Si solo está presente la instanciación del método ([1]), el compilador solo generará ese método, y dejará bar indefinido. Por lo tanto, cualquier código que incluya test.h, enlaces contra el test.cpp compilado y solo utilice el método foo compilará y vinculará, pero el uso de bar no podrá enlazarse debido a que no está definido.

La instanciación explícita de la plantilla de clase genera los símbolos para todos los métodos de miembro, y en ese caso, cualquier unidad de traducción que incluya test.h y enlaces contra el archivo de objeto test.cpp compilado compilarán y vincularán.

+0

tengo dos dudas 1. ABC es como plantilla por qué no mostrar error cuando ponemos defination de la función miembro de la clase ABC en test.cpp 2. Si pongo código test.cpp en test.h y quitar 2 entonces trabajar bien ¿puede explicar – Suri

+0

? No es necesario que las definiciones de métodos de plantilla estén dentro del cuerpo de la clase, incluso si se recomienda. Al igual que con las funciones normales, si el compilador solo ve la declaración del método, asumirá que está definida en otro lugar, como en el ejemplo de cpp. Este enfoque es problemático porque limita los tipos que se pueden usar con su plantilla a aquellos para los que proporciona una especialización explícita. Esto se puede usar para acelerar el tiempo de compilación para código con muchas plantillas para el cual se conocen con anticipación todos los tipos que instanciarán la plantilla. –

+0

Sobre la segunda pregunta, si se presiona todo el código juntos, el compilador ve la definición de plantilla (método), por lo que cuando se llama al método 'bar' sean en el principal se llevará a cabo una instancia implícita de ese método. Tenga en cuenta que la creación de instancias explícita solo es necesaria si el compilador no puede crear una instancia implícita de la plantilla, y que en la mayoría de los casos si el compilador tiene suficiente información, al permitirle decidir qué métodos crear (solo los utilizados) mejorará los tiempos de compilación (y ficheros objeto, aunque en la mayoría de los casos, el enlazador puede descartar símbolos no utilizados) –

0

(Esta es una versión editada de mi respuesta original, impulsado por la observación de David Rodríguez.)

#1 una instancia de la plantilla de clase, y como parte de una instancia que todos sus métodos.

#2 crea un método de miembro de la clase. Como parte de eso, tiene que crear una instancia de la plantilla de la clase, pero no de todos sus otros métodos.

La diferencia se puede ver si introduce un error dependiente del tipo en la barra() (por ejemplo, una afirmación como void *x = b;). Obtendrá un error de compilación con #1 pero no con #2. También tenga en cuenta que el compilador (al menos gcc) no compilaría #1 seguido de #2, pero compilaría cualquiera de ellos sin el otro, o si #2 fuera seguido por #1.

+0

Al crear instancias de plantillas de clase, solo se crean instancias de miembros _used_. Por eso creo que tu # 2 está equivocado. La respuesta de David también lo dice. – sbi

0

Supongo que quería tener {} en lugar de; En 1.

Cuestiones relacionadas