views:

250

answers:

2

I have a template class with a template member function. I want to explicitly instantiate the class to avoid a drastic compilation slowdown. I am using g++ 4.1.2. I get ambiguous template specialization errors from the compiler. This the shortest code which will reproduce the problem:

template <class T, class S >
class Test
{
public:
 template< typename T1 > 
 void* get( const T1& );
 void* get(const int& ); //Specialization of the above
};

typedef Test<int, double> foo;

//instantiate
inline template class Test<int, double>;
template void* foo::get(int const&);

I don't want to use a catch-all:

template class Test<int, double>

because the overload get(const int&) will not be defined for all possible explicit instantiations and hence the compiler will throw a fit for types which dont support it.

This code compiles in visual studio (without the inline preceeding template, which is a gcc specific extenstion). Can somebody please throw some light into how I get this code fragment to compile?

UPDATE: This is the error I get:

g++    -c -o template.o template.cpp
template.cpp:14: error: ambiguous template specialization ‘get<>’ for ‘void* Test<int, double>::get(const int&)’
template.cpp:7: error: candidates are: void* Test<T, S>::get(const int&) [with T = int, S = double]
template.cpp:6: error:                 template<class T1> void* Test::get(const T1&) [with T1 = T1, T = int, S = double]

UPDATE2: Thanks for the solution, it does not compile though. Specializations are not allowed inside a class. The error is:

g++    -c -o template.o template.cpp
template.cpp:7: error: explicit specialization in non-namespace scope ‘class Test<T, S>’
template.cpp:7: error: enclosing class templates are not explicitly specialized
template.cpp:8: error: ‘get’ is not a template function
template.cpp: In instantiation of ‘void* Test<T, S>::get(const T1&) [with T1 = int, T = int, S = double]’:
template.cpp:15:   instantiated from here
template.cpp:15: error: explicit instantiation of ‘void* Test<T, S>::get(const T1&) [with T1 = int, T = int, S = double]’ but no definition available
make: *** [template.o] Error 1
+2  A: 

I'm confused by this:

I don't want to use a catch-all:

template class Test<int, double>

because the overload get(const int&) will not be defined for all possible explicit instantiations and hence the compiler will throw a fit for types which dont support it.

Explicitness of one specialization does not affect the semantics of other specializations.

The overload get(const int&) is simply a member function, and it will be instantiated for explicit and implicit specializations like any other.

Explicit instantiation can only slow down the compiler. It only processes each instantiation at most once. Unused parts of an implicit instantiation may be ignored, but by explicitly instantiating, you are forcing it to handle the whole thing. Not that a single instantiation is likely to take a measurable amount of time, anyway.

To run through errors in the code:

template <class T, class S > // arguments unused
class Test
{
public:
 template< typename T1 > 
 void* get( const T1& );
 void* get(const int& ); // overload of the above
};

typedef Test<int, double> foo;

// illegal to instantiate a template before its members are defined

inline template class Test<int, double>; // "inline template" is meaningless
template void* foo::get(int const&); // typedef name "foo" cannot be used here
/* ^ illegal to explicitly instantiate a member of an already explicitly 
   instantiated template */

Update:

The error results from the non-template overload not having priority over the member template.

Unfortunately, you cannot explicitly specialize a member template of a template parent. The workaround at that question is to partially specialize, but that won't work because you have a function template.

Workaround #2 is SFINAE.

 #include <boost/enable_if.hpp>
 #include <boost/type_traits.hpp>

 template< typename T1 > 
 boost::disable_if< boost::is_same<T1,int>, void* >::type
     get( const T1& ); // "turn off" declaration if in conflict

 void* get(const int& ); // unambiguous overload of the above

If you can't use Boost,

 template< class T >
 struct disable_if_int { typedef void *type; };

 template<>
 struct disable_if_int<int> {};

...

 template< typename T1 > 
 disable_if_int< T1 >::type get( const T1& );

 void* get(const int& ); // unambiguous overload of the above
Potatoswatter
In gcc, if you instantiate with inline, then no member functions are intantiated and you can do it on need basis. If you do not use "inline" while instantiating, then all the members of the template are instantiated. Ref: http://gcc.gnu.org/onlinedocs/gcc/Template-Instantiation.htmlSorry for the bad phrasing, I wanted the capability to instantiate every necessary member by using inline and got the error when trying to do so.
Venkatesan
@Venkatesan: "every necessary member"? According to the page you linked, no members are instantiated by that.
Potatoswatter
A: 
template <class T, class S >
class Test
{
public:
 template< typename T1 > 
 void* get( const T1& ) { return nullptr; }

 template <>
 void* get<int>(const int& ) { return nullptr; } //Specialization of the above
};

typedef Test<int, double> foo;

int main() {
    foo lols;
    void* innit = lols.get(1);
    void* bruv = lols.get("yocakes");
}

This compiles just fine for me on VS2010. nullptr is c++0x btw, but you could just replace with 0/NULL.

DeadMG
14.7.3/2: "An explicit specialization shall be declared… for member templates, in the namespace of which the enclosing class or enclosing class template is a member."Also, you removed the explicit instantiations entirely, which kinda sidesteps the question.
Potatoswatter