views:

981

answers:

5

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?

+3  A: 

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.

Fred Larson
Are there situations where it would behave differently? Is it a potential bug, or just bad style?
Shmoopty
Usually, this is the right thing to do. You should usually prefer plain overloads over function template specializations. The behavior is more predictable.
jalf
+1 for the link, just what I was looking for.
avakar
+2  A: 

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.

Igor Krivokon
+3  A: 

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.

Luc Touraille
+13  A: 

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
}
David Rodríguez - dribeas
I believe that "template <> void operator()( int data ) {" in the top code section should be "template <> void operator()<int>( int data ) {", and in the bottom section, "int g() {" should be "int g<int>() {" in the bottom(sorry, I don't know how to stylize code sections in comments)
James Caccese
I had a doubt there, but both GCC and Comeau compilers take the code as valid. I cannot test MSVS now, if you can give it a try, I would appreciate it :)
David Rodríguez - dribeas
still have a look at the answer here http://stackoverflow.com/questions/937744/function-template-specialization-format
stefanB
I have. I did look at it and back to the standard and retested with the compilers. The extra pair of brackets <> is required if you are specializing a template for a type that already has a non-templated signature. The compiler will get mixed up with the two signatures at the point of specialization (will try the non-template and will fail to specialize a non-templated member function).
David Rodríguez - dribeas
@James. The compiler is able to deduce 'T' for the explicit specializations by looking at the parameter type, therefore it's not necessary (other than as a style preference) to explicitly specify the template arguments in this case. In this case, there's actually a core issue to allow deduction from the return type, something not normally possible: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#621
Richard Corden
@Richard: Thanks for pointing the issue on the standard.
David Rodríguez - dribeas
+2  A: 

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
stefanB
You miss the argument brackets: template<> void doh::operator()<>(bool b) (note the "<>" i inserted), and note that he (sutter) explicitly writes that the most specialized base-template is selected. Not "the one with the most matching specialization" nor "the most specialized specialization of templated function". The resolution explicitly only considers primary (base) templates. This is called "partial ordering of function templates" and is described at 14.6.6.2 in the standard.
Johannes Schaub - litb
You're right about selection of base templates, I guess I short-circuited it mine explanation (lazy typing?), I was trying to say 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
stefanB
On gcc3.3.3 both with brackets template<> void doh::operator()<>(bool b) and without forms template<> void doh::operator()(bool b) compile and gave me the same result, still copy/paste of the example by @dribeas will give errors error: explicit specialization in non-namespace scope `struct doh' and so on - this is what I was referring to (and because the methods are all private but that's on side note)
stefanB
I fixed the explanation (I hope)
stefanB
Could you explain the reason behind the second bracket <> in: template<> void doh::operator()<>(bool b) ?
stefanB
Otherwise, the non-template operator() function will get picked and specialized, since it is preferred in case of an equally good match with the template function. But that will result in an error, since non-template functions can't be specialized. You will thus tell the compiler that it has to limit lookup only to templates, which is possible using an empty "<>" if all arguments can be deduced
Johannes Schaub - litb
Actually, GCC accepts it without "<>", but comeau rejects it for the reasons i said (actually, i wasn't sure either, so i tested it first, using comeau before i told you, hehe). It looks natural to me that it fails, but this seems to be a tricky issue to me. Maybe worth a SO question on its own.
Johannes Schaub - litb
Done: http://stackoverflow.com/questions/937744/function-template-specialization-format
stefanB
I have just fixed the three errors in my code. The two errors you detected and an extra error I did not notice myself. It now compiles fine both with gcc 4.3 and Comeau. The latter being the best reference for a standard compliant compiler.
David Rodríguez - dribeas
I believe that the extra pair of angle brackets is optional in 'template<> void doh::operator()<>(int i)' while it is required in the specialization for bool.
David Rodríguez - dribeas
@dribeas, i agree - no need to put "<>" for int. But in any case, in my own code i would go explicit and always put any arguments in specializations, so that there are no surprises :)
Johannes Schaub - litb