2010-09-13 18 views
12

que pensé una solicitud explícita de instancias sería una instancia de forma automática todos los miembros de la clase base también, pero me da un linker error: unresolved external symbol "public: void Base<int>::foo(int)" en la construcción de este código utilizando Visual Studio 2008 o 2010.Hacer instancias explícitas de plantillas de clase C++ Crear una instancia de clases base dependientes?

Tenga en cuenta que la adición de una llamada a foo() dentro bar() obliga al compilador instanciar Base<int>::bar() y la compilación tiene éxito, por lo que parece que el compilador tiene toda la información necesaria para crear una instancia de foo().

Obviamente, instanciar explícitamente Base<int> en source.cpp permite que la compilación tenga éxito, pero parece una tontería tener que instanciar explícitamente cualquier clase base dependiente siempre que ejemplifique explícitamente una clase derivada.

¿Es esto normal? No pude encontrar lo que dice la norma con respecto a este tema.

header.h

template<typename T> 
class Base { 
public: 
    void foo(); 
}; 

template<typename T> 
class Derived : public Base<T> { 
public: 
    void bar(); 
}; 

source.cpp

#include "header.h" 

template<typename T> 
void Base<T>::foo() { } 

template<typename T> 
void Derived<T>::bar() { 
    // this->foo(); // adding this forces instantiation of foo()??? 
} 

template class Derived<int>; 

main.cpp

#include "header.h" 

int main() { 
    Derived<int> d; 
    d.foo(); // Linker Error: unresolved external symbol "public: void Base<int>::foo(int)" 
} 

Editar:

Parece que el estándar dice que solo los miembros de una clase son instanciados por una instancia de clase explícita, por lo que el error del enlazador se justifica en mi ejemplo.

Tenga en cuenta que una clase se define por class-head {member-specification} y "La especificación miembro en una definición de clase declara el conjunto completo de miembros de la clase; ningún miembro se puede agregar en otro lugar". Por lo tanto, los miembros solo se encuentran entre llaves {}, y los miembros de la clase base pública no se convierten en miembros de la clase derivada, simplemente son accesibles desde la clase derivada o por los objetos de la clase derivada.

Mi única pregunta restante es por qué el estándar especifica que la creación de instancias explícita de una plantilla de clase solo instancia los miembros y no los miembros de las clases base? Mi suposición es que esto permite un mayor control de lo que se ejemplifica explícitamente donde. Alguien que está usando instancias explícitas de clases de plantillas muy probablemente tenga las definiciones de clase base en un archivo diferente de las definiciones de clases derivadas, y instanciaría explícitamente cada una por separado.

+0

¿Agregar un constructor explícito para la creación de instancias derivadas de la clase Base? Sospecho que el compilador está tratando de no hacer más trabajo del necesario. Tener un constructor puede hacer que se dé cuenta de que también necesita construir Base, y eso podría desencadenarlo. Además, ¿has probado gcc u otros compiladores? –

+0

Agregar un constructor con o sin un argumento y con o sin la palabra clave "explicit" no desencadena la instanciación de la clase base foo(), pero una llamada a foo() desde la barra miembro derivada() desencadena la instanciación implícita. – JohnPS

Respuesta

9

La norma dice

La creación de instancias explícita de una especialización de plantilla de clase implica la creación de instancias de todos sus miembros que anteriormente no especializados explícitamente en la unidad de traducción que contiene la creación de instancias explícita.

En otras palabras, no exige que las clases base se ejemplifiquen explícitamente. Causará una instanciación implícita de ellos que no instanciará sus definiciones de miembros por adelantado.Es un error desagradable en el estándar en cuanto a si algún texto cuando dice "miembro" significa miembro "directo" o "heredado", ya que a menudo parece ser "obvio" para quien escribió la redacción de Estándares, pero no para el quien lo lee C++ 0x ha agregado algunas aclaraciones (también tiene una diferencia entre la instanciación explícita declaraciones y definiciones que C++ 03 no tiene, pero incluso ignorando eso, la redacción C++ 0x contiene algunos más bits de insight):

una instanciación explícita que nombra a una especialización de plantilla de clase es también una instanciación explícita de la mismo tipo (declaración o definición) de cada uno de sus miembros (no incluidos los miembros heredados de la base clases) que tiene no se ha especializado previamente explícitamente en la unidad de traducción que contiene la instanciación explícita , excepto como se describe a continuación. [Nota: Además, típicamente será una instanciación explícita de de ciertos datos dependientes de la implementación sobre la clase. - nota final]

+0

+1. ¿No hay una declaración general más amplia que puedan hacer, como que es indistinguible de la definición/declaración de una clase y todos sus miembros estáticos? Dejarlo abierto ya sea que el vtable sea instanciado es fealdad y lo reconocen. – Potatoswatter

+1

+1 El estándar me confundió mediante el uso de "especialización de plantilla de clase" para significar una entidad producida por "instanciación explícita" frente a "especialización explícita" usando plantilla <>. Supongo que no crear instancias de miembros de base permite un mayor control de lo que se instancia y dónde va. Los miembros base podrían ser instanciados en un archivo fuente diferente de la clase derivada. – JohnPS

+0

@Potatoswatter: ¿No solo necesitamos la declaración de la clase base para determinar el tamaño del vtable de la clase derivada durante la instanciación de la clase derivada, y el enlazador rellena los valores en su vtable? Si alguna función llamada no se instancia en alguna unidad de traducción, entonces habrá un error de enlazador. – JohnPS

Cuestiones relacionadas