Question

I have a visitor class resembling this:

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

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

Clearly, operator()(bool b) is intended to be a specialization of the preceding template function.

However, it doesn't have the template<> syntax that I'm used to seeing before it, declaring this as a template specialization. But it does compile.

Is this safe? Is this correct?

Was it helpful?

Solution

Your code is not a template specialization, but rather a non-templated function. There are some differences there. The non-templated operator() will take precedence over a templated version (for an exact match, but type conversions will not take place there) but you can still force the templated function to be called:

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
}

In your code there is not much of a difference, but if your code needs to pass the template parameter type it will get funnier:

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
}

OTHER TIPS

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
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top