2012-01-09 14 views
11

En la muestra:C++ especialización de clase de plantilla: ¿por qué los métodos comunes necesitan ser re-implementado

#include <iostream> 

using namespace std; 

class B 
{ 
public: 
    virtual void pvf() = 0; 
}; 

template <class T> 
class D : public B 
{ 
public: 
    D(){} 

    virtual void pvf() {} 

private: 
    string data; 
}; 

template <> 
class D<bool> : public B 
{ 
public: 
    D(); 

    virtual void pvf(){ cout << "bool type" << endl; } 
}; 

int main() 
{ 
    D<int> d1; 
    D<bool> d2; 
} 

me sale el siguiente error:

test.cpp:(.text+0x1c): undefined reference to `D<bool>::D()' 

Tenga en cuenta que la razón por la que don' Sólo especializo D() por si mismo. Quiero eliminar la necesidad de la cadena D<T>::data en el caso D<bool>.

¿Por qué tengo que volver a implementar D() en D<bool>? Parece que debería haber una forma de decirle al compilador que use la versión del D<T>.

¿Hay alguna manera de hacer una especialización simple como esta sin tener que volver a implementar los métodos?

+0

Porque es un tipo completamente nuevo. –

Respuesta

10

No, no lo hay.

La especialización se comporta de manera muy diferente que la herencia. No tiene conexión con la versión genérica de la plantilla.

Cuando utiliza/crea una instancia de una plantilla, el compilador creará un nuevo nombre de tipo, y luego buscará cómo se define este tipo. Cuando encuentra una especialización, toma eso como la definición para el nuevo tipo. Cuando no lo hace, toma la plantilla genérica y la crea.

Por lo tanto, no tienen conexión, y usted está escribiendo una clase completamente nueva, solo con un nombre especial para que el compilador encuentre en caso de que alguien use/instanciando la plantilla para encontrarlo con ese nombre.

+7

Eso es desafortunado – Jaime

1

Necesita volver a implementarlo porque D<T> y D<bool> son clases totalmente independientes (simplemente pasan a "compartir el nombre"). Así es cómo funcionan las plantillas.

Si desea que las clases compartan el código de construcción, simplemente ponga ese código en B::B (es decir, lo mismo que hace cada vez que quiere reutilizar código en diferentes ramas de la misma jerarquía: mueva el código y deje que la herencia maneje el resto).

12

Cada especialización de una plantilla de clase ofrece una clase diferente: no comparten ningún miembro entre sí. Dado que usted ha especializado explícitamente a toda la clase, no obtiene ninguno de los miembros de la plantilla y debe implementarlos todos.

Usted puede especializarse explícitamente a los miembros individuales, en lugar de toda la clase:

template <> void D<bool>::pvf(){ cout << "bool type" << endl; } 

Entonces D<bool> seguirá conteniendo todos los miembros de la plantilla de clase que no tiene explícitamente especializada, incluyendo el constructor por defecto.

+0

Me pegó por 21 segundos (pero solo porque tuve que probar esto para asegurarme de que fuera posible :)) +1 –

+0

Soy consciente de que puedes especializar métodos, es por eso que puse la nota sobre por qué ' No estoy haciendo eso ... Mi objetivo es tener un miembro de datos en uno pero no en el otro y no tener que volver a implementar métodos comunes. Entiendo lo que está pasando, simplemente no entiendo por qué el método común no puede ser reutilizado. – Jaime

4

El problema es su suposición errónea de que hay algo común entre D<A> y D<B>. Las instancias de plantilla son tipos, y dos instancias diferentes son dos tipos diferentes, al final de la historia. Solo sucede que las instancias de la misma plantilla tienen un código formalmente similar, pero con especialización puede definir cualquier tipo que desee.En resumen, cada tipo que defina explícitamente es completamente independiente, y no hay elementos comunes en las instancias de plantilla especializadas, incluso si tienen el mismo nombre.

Por ejemplo:

template <typename T> struct Foo 
{ 
    T & r; 
    const T t; 
    void gobble(const T &); 
    Foo(T *); 
}; 

template <> struct Foo<int> 
{ 
    std::vector<char> data; 
    int gobble() const; 
    Foo(bool, int, Foo<char> &); 
}; 

Los tipos Foo<char> y Foo<int> tienen nada que ver uno con el otro, y no hay razón por la cual ninguna parte de uno debe tener cualquier uso dentro de la otra.

Si desea factorizar características comunes, utilizar la herencia privada:

template <typename> struct D : private DImpl { /* ... */ } 
+0

Deja de tomar mis pensamientos y formarlos como tu respuesta! :( – Xeo

+0

@Xeo: ¡Ponte la tapa de lata! –

+0

Sin hacer ninguna suposición, solo preguntándome por qué no puedo decirle al compilador que use la implementación predeterminada para la plantilla. Entiendo que cada tipo de plantilla es de un tipo diferente, independientemente de la especialización , pero eso va a mi punto. Si el compilador puede usar el mismo método para ambos tipos, ¿por qué no para una especialización siempre que no haya conflictos? – Jaime

0

Considere que D<T>::D() será responsable de la construcción de default-string data, y que D<bool> no tiene ningún dicho miembro. Claramente, no hay forma de usar el mismo código emitido en cada caso.

Sin embargo, si su constructor predeterminado no hace nada (en ya sea versión aquí), simplemente omítalo y permita que el compilador haga el trabajo.

+0

Mismo problema con un método que no sea el constructor. – Jaime

Cuestiones relacionadas