Pregunta

Tengo una clase de visitantes que se asemeja a esto:

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

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

Claramente, operator()(bool b) está destinado a ser una especialización de la función de plantilla anterior.

Sin embargo, no tiene el template<> Sintaxis que estoy acostumbrado a ver antes, declarando esto como una especialización de plantillas. Pero se compila.

¿Es esto seguro? ¿Es esto correcto?

¿Fue útil?

Solución

Su código no es una especialización de plantillas, sino una función no templada. Hay algunas diferencias allí. El operador no templado () tendrá prioridad sobre una versión plantada (para una coincidencia exacta, pero las conversiones de tipo no tendrán lugar allí) pero aún puede forzar la función plantada a 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 necesita pasar el tipo de parámetro de plantilla, será 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
}

Otros consejos

Lo que tienes aquí es sobrecarga de funciones; Para obtener especialización de plantillas, de hecho necesita el template <> sintaxis. Sin embargo, debe ser consciente de que estos dos enfoques, incluso si pueden parecer idénticos, son sutilmente diferentes, e incluso el compilador podría 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 verificar Herb Sutter Gotw #49 sobre el tema.

Oh, se compilará. Simplemente no será una función de plantilla. Tendrá una función regular sin plantilla en lugar de una especialización de plantilla.

Es seguro y, en realidad, también lo que quieres. El patrón de visitante normalmente se implementa sobrecargando. Especialización de plantillas de funciones No es realmente una buena idea de todos modos.

Lo que hiciste no es plantilla de serialización, sino que funciona la sobrecarga. Es seguro.

PD: Es difícil decir si es correcto o no, sin saber lo que está tratando de lograr. Tenga en cuenta que no importa, 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 sepas de todos modos; por si acaso.

Tú tienes

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

Podrías tener una especialización completa del segundo como en template<> void operator(int i) que solo se consideraría cuando void operator()(bool b) no emparejaron.

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

El artículo ¿Por qué no especializar las plantillas de funciones? Da bastante buena explicación de cómo se selecciona el método.

En Sumary:

  1. Las funciones sin plantilla se consideran primero (este es su operador simple () (bool) anterior)
  2. Las plantillas de base de funciones se verifican en segundo lugar (esta es su función plantada), se selecciona la plantilla base más especializada y luego si tiene especialización para los tipos exactos de 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);
}

Recibes 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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top