Question

J'ai un cours de visiteur qui ressemble à ceci:

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

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

Clairement, operator()(bool b) est destiné à être une spécialisation de la fonction de modèle précédente.

Cependant, il n'a pas le template<> Syntaxe que j'ai l'habitude de voir avant elle, déclarant cela comme une spécialisation de modèle. Mais il compile.

Est-ce sûr? Est-ce correct?

Était-ce utile?

La solution

Votre code n'est pas une spécialisation de modèle, mais plutôt une fonction non compliquée. Il y a quelques différences là-bas. L'opérateur non tentant () aura la priorité sur une version modèle (pour une correspondance exacte, mais les conversions de type n'auront pas lieu là-bas), mais vous pouvez toujours forcer la fonction modèle à appeler:

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
}

Dans votre code, il n'y a pas beaucoup de différence, mais si votre code doit passer le type de paramètre de modèle, il deviendra plus drôle:

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
}

Autres conseils

What you have here is function overloading; to obtain template specialization, you indeed need the template <> syntax. However, you should be aware that these two approaches, even if they may seem identical, are subtly different, and even the compiler might get lost when choosing the right function to call. Listing all the possible cases would be a little too long for this answer, but you might want to check Herb Sutter GoTW #49 on the subject.

Oh, it'll compile. It just won't be a template function. You'll have a regular non-template function instead of a template specialization.

It's safe, and actually likely what you want as well. The Visitor pattern is normally implemented by overloading. Specializing function templates isn't really a good idea anyway.

What you did is not template serialization, but function overloading. It is safe.

P.S. It's difficult to say whether it's correct or not, without knowing what you're trying to achieve. Keep in mind that no matter is it template or overloaded function, your operator will be chosen in compile time. If you need to run-time dispatch, you need polymorphism, not overloading. Well, you probably know it anyway; just in case.

You have

  • void operator()(bool b) that is non templated function
  • template< typename T > void operator()(T t) which is a separate base template that overloads the above

You could have a full specialization of the second one as in template<> void operator(int i) which would only be considered when void operator()(bool b) did not match.

The specialization of base template is used to select which of the base template methods to call. However in your case you have a non-templated method that will get considered first.

The article Why Not Specialize Function Templates? gives quite good explanation of how the method is selected.

In sumary:

  1. Non template functions are considered first (this is your plain operator()(bool) above)
  2. Function base templates get checked second (this is your templated function), the most specialized base-template is selected and then if it has specialization for the exact types that specialization is used otherwise the base template is used with 'the correct' types (see explanation in the article)

Example:

#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);
}

You get calls to:

operator()(bool b)       <-- first non template method that matches
template <> void operator()(int i)     <-- the most specialized specialization of templated function is called
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top