views:

1453

answers:

1

What is the reason for the second brackets <> in the following function template:

template<> void doh::operator()<>(int i)

This came up in SO question where it was suggested that there are brackets missing after operator(), however I could not find the explanation.

I understand the meaning if it was a type specialization (full specialization) of the form:

template< typename A > struct AA {};
template<> struct AA<int> {};         // hope this is correct, specialize for int

However for function templates:

template< typename A > void f( A );
template< typename A > void f( A* ); // overload of the above for pointers
template<> void f<int>(int);         // full specialization for int

Where does this fit into this scenarion?:

template<> void doh::operator()<>(bool b) {}

Example code that seems to work and does not give any warnings/error (gcc 3.3.3 used):

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

Output:

operator()(bool b)
template <> void operator()(int i)
+8  A: 

I've looked it up, and found that it is specified by 14.5.2/2:

A local class shall not have member templates. Access control rules (clause 11) apply to member template names. A destructor shall not be a member template. A normal (non-template) member function with a given name and type and a member function template of the same name, which could be used to generate a specialization of the same type, can both be declared in a class. When both exist, a use of that name and type refers to the non-template member unless an explicit template argument list is supplied.

And it provides an example:

template <class T> struct A {
    void f(int);
    template <class T2> void f(T2);
};

template <> void A<int>::f(int) { } // non-template member
template <> template <> void A<int>::f<>(int) { } // template member

int main()
{
    A<char> ac;
    ac.f(1); //non-template
    ac.f(’c’); //template
    ac.f<>(1); //template
}

Note that in Standard terms, specialization refers to the function you write using an explicit specialization and to the function generated using instantiation, in which case we have to do with a generated specialization. specialization does not only refer to functions you create using explicitly specializing a template, for which it is often only used.

Conclusion: GCC gets it wrong. Comeau, with which i also tested the code, gets it right and issues a diagnostic:

"ComeauTest.c", line 16: error: "void doh::operator()(bool)" is not an entity that can be explicitly specialized template<> void doh::operator()(bool i)

Note that it isn't complaining about the specialization of the template for int (only for bool), since it doesn't refer to the same name and type: The function type that specialization would have is void(int), which is distinct from the function type of the non-template member function, which is void(bool).

Johannes Schaub - litb
So if I follow correctly the 'template member' above is a templated member in "specialization of struct A for int". Make sense now, so calling ac.f() will do it's own template resolution based on the parameter or explicit template parameter list (whatever the correct name is).
stefanB
yeah well the explicit specialization we provided for f is for the specialization of struct A for int. the general base template f exist for all specializations of A. That's why we can also call it on a A<char>, as it does in main, of course (but it won't use our explicit specialization, since that was only for A<int>::f<int>!) . Yeah ac.f(stuff) does its own resolution, indeed. Here, consult 14.8.1/2 ("Trailing template arguments that can be deduced..."). Hope this helps :)
Johannes Schaub - litb
Yes great, thanks for the research :)
stefanB
I was just thinking that you could scare people by showing them this especially if they don't know c++ and templates syntax: template <> template <> void A<int>::f<>(int) { } or even better: template <> template <> void A<int>::operator()<>(int) { }
stefanB
@stefanB, ahaha :P indeed, it looks suspicious. In any case, please bear with me - my last comment (now removed) said that for a primary (base) template definition, an explicit argument list shall be provided: Now getting up, i of course see that was nonsense all the way :)
Johannes Schaub - litb
In `template <> template <> void A<int>::f<>(int) { }`, isn't the `<>` just before the parameter list redundant? The compiler already knows you are referring to the template version of the member because of the `template <> template <>`.