2010-03-23 20 views
12

Cuando especializo una función/constante de miembro (estática) en una clase de plantilla, no estoy seguro de a dónde debe ir la declaración.Declaración de especialización de miembro de clase de plantilla

He aquí un ejemplo de lo que lo haga - yoinked directamente de IBM's reference on template specialization:

=== IBM miembro especialización Ejemplo ===

template<class T> class X { 
public: 
    static T v; 
    static void f(T); 
}; 

template<class T> T X<T>::v = 0; 
template<class T> void X<T>::f(T arg) { v = arg; } 

template<> char* X<char*>::v = "Hello"; 
template<> void X<float>::f(float arg) { v = arg * 2; } 

int main() { 
    X<char*> a, b; 
    X<float> c; 
    c.f(10); // X<float>::v now set to 20 
} 

La pregunta es, ¿cómo puedo dividirlo en archivos de cabecera/cpp? La implementación genérica está obviamente en el encabezado, pero ¿qué pasa con la especialización?

No puede ir en el archivo de encabezado, porque es concreto, lo que lleva a una definición múltiple. Pero si entra en el archivo .cpp, ¿está el código que llama a X :: f() al tanto de la especialización, o podría depender del genérico X :: f()?

Hasta ahora tengo la especialización en el .cpp solamente, sin declaración en el encabezado. No tengo problemas para compilar o incluso ejecutar mi código (en gcc, no recuerdo la versión en este momento), y se comporta como se esperaba, reconociendo la especialización. Pero A) No estoy seguro de que esto sea correcto, y me gustaría saber qué es, y B) mi documentación de Doxygen sale poco clara y muy engañosa (más sobre eso en un momento una pregunta posterior).

Lo que parece más natural para mí sería algo como esto, declarando la especialización en la cabecera y definirlo en el .cpp:

=== === XClass.hpp

#ifndef XCLASS_HPP 
#define XCLASS_HPP 

template<class T> class X { 
public: 
    static T v; 
    static void f(T); 
}; 

template<class T> T X<T>::v = 0; 
template<class T> void X<T>::f(T arg) { v = arg; } 

/* declaration of specialized functions */ 
template<> char* X<char*>::v; 
template<> void X<float>::f(float arg); 

#endif 

=== XClass.cpp ===

#include <XClass.hpp> 

/* concrete implementation of specialized functions */ 
template<> char* X<char*>::v = "Hello"; 
template<> void X<float>::f(float arg) { v = arg * 2; } 

... pero no tengo idea si esto es correcto. ¿Algunas ideas?

Respuesta

9

Por lo general, solo debe definir las especializaciones inline en el encabezado como se dijo dirkgently.

Puede definir especializaciones en unidades de traducción separadas, aunque si usted está preocupado por los tiempos de compilación o hinchazón de código:

// x.h: 
template<class T> struct X { 
    void f() {} 
} 

// declare specialization X<int>::f() to exist somewhere: 
template<> void X<int>::f(); 

// translation unit with definition for X<int>::f(): 
#include "x.h" 
template<> void X<int>::f() { 
    // ... 
} 

Así que sí, el enfoque se ve bien. Tenga en cuenta que solo puede hacer esto con especializaciones completas, por lo que a menudo no es práctico hacerlo.

Para obtener más información, consulte p. Ej. Comeaus template FAQ.

+1

Pregunta, por curiosidad: ¿por qué la preferencia por la inclusión? Parece un "requisito" extraño de imponer: que "por defecto" una especialización de plantilla estaría en línea. Pregunto todo esto solo porque insinúas que eso es lo que "normalmente" harías, y no veo por qué normalmente querría hacer eso más de lo que quisiera declarar cualquier otra función en línea. – Ziv

+0

Se trata de la preferencia de ponerlos en encabezados, 'inline' es un efecto secundario. Menos para escribir, más oportunidades de optimización, sin errores al olvidar una declaración. También siempre necesita especializarse completamente y para la mayoría de las plantillas eso significaría agregar una declaración e instanciación explícita para cada combinación de tipos con los que se usa. Tipo de derrota el intento de genericidad, ¿no? –

+0

Bueno, por supuesto, solo estoy hablando de casos en los que una especialización completa es lo que se desea, de lo contrario obviamente es una historia completamente diferente ... Da la vuelta a la pregunta, entonces: ¿por qué esas mismas ventajas no nos llevan a preferir generalmente? escribe todas las funciones en línea? – Ziv

4

Colóquelos todos en un archivo hpp. Realice las especializaciones y todo lo que defina fuera de la clase inline, que se ocupará de múltiples definiciones.

+0

Interesante. Pero esto no funciona para variables estáticas, solo para funciones. Aparte de eso, ¿no hay una manera más elegante/estándar para lidiar con esto? La especialización de miembros de la clase de plantilla es una parte importante de la programación de plantillas. Seguramente "¿dónde pones la declaración?" Tiene una respuesta más directa que "declara en línea y engaña al compilador". – Ziv

+0

@Ziv: Pensé que las variables estáticas no funcionarían en los encabezados, pero lo he intentado en VS2008 y parece que funciona. – quamrana

+1

Las especializaciones se pueden definir en un archivo fuente o en línea en un encabezado, pero deben declararse en un encabezado. –

0

Para responder a una de sus preguntas: is code which calls X::f() aware of the specialization, or might it rely on the generic X::f()?

Si el compilador ve una definición que coincide con sus requisitos, entonces se va a usar. De lo contrario, generará llamadas a funciones normales.

En el primer fragmento de código, proporciona una definición genérica para X<T>::f(T arg), por lo que el compilador instanciará eso para cualquier T aparte de float.

Si omitiera la definición genérica, el compilador generaría llamadas a, por ejemplo, X<double>::f(double) y el enlazador buscaría la definición que podría terminar con un error de enlazador.

En resumen: puede tener todo en los encabezados, porque como plantillas no obtendrá múltiples definiciones. Si solo tiene declaraciones, necesitará definiciones en otro lugar para que el enlazador las encuentre más adelante.

+0

Mi pregunta es al revés: si/dónde/cómo poner la declaración especializada. Una plantilla de definición de especialización * no puede * ir en el encabezado (a menos que esté en línea, como se sugiere dirkgently). ¿Cómo me aseguro de que X :: f() llame a la especialización y no llame erróneamente la definición genérica? – Ziv

+0

@Ziv: Mis experimentos, contrariamente a mis expectativas, dicen que todo * puede * ir en el encabezado, especializaciones estáticas y todo. – quamrana

+0

Eso es extraño. Intenté eso, y recibí un error de "definición múltiple ...". ¡Conformidad del compilador de Yay! – Ziv

Cuestiones relacionadas