2009-06-01 4 views
8

tengo una clase visitante se asemeja a esto:¿Las especializaciones de plantilla requieren una sintaxis de plantilla <>?

struct Visitor 
{ 
    template <typename T> 
    void operator()(T t) 
    { 
     ... 
    } 

    void operator()(bool b) 
    { 
     ... 
    } 
}; 

Claramente, operator()(bool b) pretende ser una especialización de la función de plantilla anterior.

Sin embargo, no tiene la sintaxis template<> que yo estoy acostumbrado a ver antes de que, declarando esto como una especialización de plantilla. Pero compila.

¿Esto es seguro? ¿Es esto correcto?

Respuesta

19

Su código no es una especialización de plantilla, sino más bien una función sin plantilla. Hay algunas diferencias allí. El operador no moldeado() tendrá prioridad sobre una versión de plantilla (una coincidencia exacta, pero las conversiones de tipos no tendrá lugar allí) pero todavía se puede forzar la función de plantilla que se llamará:

class Visitor 
{ 
public: // corrected as pointed by stefanB, thanks 
    template <typename T> 
    void operator()(T data) { 
     std::cout << "generic template" << std::endl; 
    } 
    void operator()(bool data) { 
     std::cout << "regular member function" << std::endl; 
    } 
}; 
template <> // Corrected: specialization is a new definition, not a declaration, thanks again stefanB 
void Visitor::operator()(int data) { 
    std::cout << "specialization" << std::endl; 
} 
int main() 
{ 
    Visitor v; 
    v(5); // specialization 
    v(true); // regular member function 
    v.operator()<bool>(true); // generic template even if there is a non-templated overload 
    // operator() must be specified there (signature of the method) for the compiler to 
    // detect what part is a template. You cannot use <> right after a variable name 
} 

En su código no hay mucha diferencia, pero si su código tiene que pasar el tipo de parámetro de plantilla que obtendrá más divertido:

template <typename T> 
T g() { 
    return T(); 
} 
template <> 
int g() { 
    return 0; 
} 
int g() { 
    return 1; 
} 
int main() 
{ 
    g<double>(); // return 0.0 
    g<int>(); // return 0 
    g(); // return 1 -- non-templated functions take precedence over templated ones 
} 
+0

Creo que "template <> void operator() (int data) {" en la sección superior del código debe ser "template <> void operator() (int data) {", y en la sección inferior, "int g() {"debería ser" int g () {"en la parte inferior (lo siento, no sé cómo estilizar las secciones de código en los comentarios) –

+0

Tenía una duda allí, pero los compiladores GCC y Comeau toman el código como válido. No puedo probar MSVS ahora, si puedes probarlo, te lo agradecería :) –

+0

todavía eche un vistazo a la respuesta aquí http://stackoverflow.com/questions/937744/function-templates-specialization-format – stefanB

4

Oh, compilará. Simplemente no será una función de plantilla. Tendrás una función regular que no sea de plantilla en lugar de una especialización de plantilla.

Es seguro, y en realidad es lo que usted también desea. El patrón Visitor se implementa normalmente por sobrecarga. Plantillas de funciones especializadas isn't really a good idea anyway.

+0

¿Hay situaciones en las que se comportaría de manera diferente? ¿Es un error potencial, o simplemente un mal estilo? –

+0

Por lo general, esto es lo correcto. Por lo general, debería preferir las sobrecargas simples sobre las especializaciones de plantillas de funciones. El comportamiento es más predecible – jalf

+0

+1 para el enlace, justo lo que estaba buscando. – avakar

2

Lo que usted hizo no es la serialización de la plantilla, sino la sobrecarga de la función. Es seguro.

P.S. Es difícil decir si es correcto o no, sin saber lo que estás tratando de lograr. Tenga en cuenta que no importa si es plantilla o función sobrecargada, su operador será elegido en tiempo de compilación. Si necesita despacho en tiempo de ejecución, necesita polimorfismo, no sobrecarga. Bueno, probablemente lo sepan de todos modos; por si acaso.

5

Lo que tienes aquí es sobrecarga de funciones; para obtener la especialización de plantilla, de hecho necesita la sintaxis template <>. Sin embargo, debe tener en cuenta que estos dos enfoques, aunque parezcan idénticos, son sutilmente diferentes, e incluso el compilador puede perderse al elegir la función correcta para llamar. Enumerar todos los casos posibles sería demasiado largo para esta respuesta, pero es posible que desee consultar Herb Sutter GoTW #49 sobre el tema.

2

tienes

  • void operator()(bool b) que no es función de plantilla
  • template< typename T > void operator()(T t) que es una plantilla base separada que sobrecarga la anterior

usted podría tener una especialización completa de la segunda como en template<> void operator(int i) que sólo se considera cuando void operator()(bool b) no encontró.

La especialización de la plantilla base se utiliza para seleccionar cuál de los métodos de la plantilla base llamar.Sin embargo, en su caso, tiene un método sin plantilla que se considerará primero.

El artículo Why Not Specialize Function Templates? da una explicación bastante buena de cómo se selecciona el método.

En suma un resumen:

  1. funciones no de plantilla se considerarse en primer lugar (esto es su operador liso() (bool) anterior)
  2. plantillas base de función se comprueban segundo (esta es su plantilla función), se selecciona la plantilla base más especializada y luego si tiene especialización para los tipos exactos que se utiliza la especialización, de lo contrario la plantilla base se usa con los tipos "correctos" (ver explicación en el artículo)

Ejemplo:

#include <iostream> 
using namespace std; 

struct doh 
{ 
    void operator()(bool b) 
    { 
     cout << "operator()(bool b)" << endl; 
    } 

    template< typename T > void operator()(T t) 
    { 
     cout << "template <typename T> void operator()(T t)" << endl; 
    } 
}; 
// note can't specialize inline, have to declare outside of the class body 
template<> void doh::operator()<>(int i) 
{ 
    cout << "template <> void operator()<>(int i)" << endl; 
} 
template<> void doh::operator()<>(bool b) 
{ 
    cout << "template <> void operator()<>(bool b)" << endl; 
} 

int main() 
{ 
    doh d; 
    int i; 
    bool b; 
    d(b); 
    d(i); 
} 

Usted recibe llamadas a:

operator()(bool b)  <-- first non template method that matches 
template <> void operator()(int i)  <-- the most specialized specialization of templated function is called 
+2

Te pierdes los paréntesis del argumento: template <> void doh :: operator() <> (bool b) (ten en cuenta el "<>" insertado), y ten en cuenta que él (sutter) explícitamente escribe que la base-template más especializada está seleccionado. No es "el que tiene la especialización más adecuada" ni "la especialización más especializada de la función de plantilla". La resolución explícitamente solo considera las plantillas primarias (base). Esto se denomina "ordenamiento parcial de plantillas de funciones" y se describe en 14.6.6.2 en el estándar. –

+0

Tiene razón acerca de la selección de plantillas base, supongo que la cortocircuité explicación de la mina (tipeo perezoso?), Estaba tratando de decir que se seleccionó la plantilla base más especializada y luego si tiene especialización para los tipos exactos que se utiliza especialización; de lo contrario, la plantilla base se usa con los tipos "correctos" – stefanB

+0

En gcc3.3.3 ambos con paréntesis template <> void doh :: operator() <> (bool b) y sin plantilla de formularios <> void doh :: operator() (bool b) compilar y me dio el mismo resultado, aún copiar/pegar de el ejemplo de @dribeas dará errores de error: especialización explícita en el ámbito de espacio de nombres 'struct doh 'y así sucesivamente - esto es a lo que me refería (y porque los métodos son todos privados, pero eso está en la nota) – stefanB

Cuestiones relacionadas