Un colega mío me contó acerca de una pequeña pieza de diseño que ha utilizado con su equipo que hizo que mi mente se pusiera a hervir. Es un tipo de rasgos de la clase que pueden especializarse en una forma muy desacoplada.truco de métodos de plantilla indefinido?
Me ha costado entender cómo podría funcionar, y todavía no estoy seguro de la idea que tengo, así que pensé en pedir ayuda aquí.
Estamos hablando de g ++ aquí, específicamente las versiones 3.4.2 y 4.3.2 (parece funcionar con ambas).
La idea es bastante simple:
1- Definir la interfaz
// interface.h
template <class T>
struct Interface
{
void foo(); // the method is not implemented, it could not work if it was
};
//
// I do not think it is necessary
// but they prefer free-standing methods with templates
// because of the automatic argument deduction
//
template <class T>
void foo(Interface<T>& interface) { interface.foo(); }
2- Definir una clase, y en el archivo de origen se especializan la interfaz de esta clase (que define sus métodos)
// special.h
class Special {};
// special.cpp
#include "interface.h"
#include "special.h"
//
// Note that this specialization is not visible outside of this translation unit
//
template <>
struct Interface<Special>
{
void foo() { std::cout << "Special" << std::endl; }
};
3- Para utilizar, es demasiado simple:
// main.cpp
#include "interface.h"
class Special; // yes, it only costs a forward declaration
// which helps much in term of dependencies
int main(int argc, char* argv[])
{
Interface<Special> special;
foo(special);
return 0;
};
Es un símbolo indefinido si ninguna unidad de traducción definió una especialización de Interface
para Special
.
Ahora, habría pensado que esto requeriría la palabra clave export
, que hasta donde sé nunca se ha implementado en g ++ (y solo se implementó una vez en un compilador de C++, con sus autores avisando a alguien que no, dado el tiempo y esfuerzo que les llevó).
sospecho que tiene algo que ver con el enlazador resolver los métodos de plantillas ...
- ¿Se han conocido nunca nada como esto antes?
- ¿Cumple con la norma o cree que es una afortunada coincidencia que funcione?
Debo admitir que estoy bastante desconcertado por la construcción ...
Por lo tanto, si lo entiendo correctamente, sería suficiente en "special.h" para incluir "interface.h" y para reenviar declarar la plantilla de especialización 'template <> struct Interface;' y luego en "main.cpp" para incluir "special.h", para que el programa esté bien formado, aunque la definición del método 'Interface :: foo' nunca aparecerá? –
@ Matthieu, ¡exactamente!Así es como puedes resolver la miseria –
Si leo esto correctamente, esto significa que cualquier programa (múltiples unidades de traducción) donde exista una especialización explícita de una plantilla para un conjunto dado de parámetros, pero en alguna otra unidad de traducción se usa la especialización implícita para el mismo conjunto de parámetros es una violación de ODR que el compilador no es necesario, y probablemente no pueda, para emitir un diagnóstico? Eso es bastante aterrador y otra buena razón para poner siempre especializaciones parciales directamente con la plantilla primaria donde sea posible. – Stewart