views:

193

answers:

2

I've got a template class with a template method within it, giving two template parameters T and U. The operation is quite expensive and is showing up in profiling to be a major use of CPU time. I could optimise it somewhat, but only for the case where T == U (which is fairly common), however I'm not sure on the syntax for doing this...

The class and method in question look like this:

template<typename T>class Foo
{
public:
    ...
    template<typename U>U bar()const;
};

Foo::bar is generally called from some other template code, so even if I created a separate method (e.g. "T fastBar()const") I don't know how id go about making the other template code call that version where possible...

I tried to create an explicit specialisation for T == U, but VC9 gave me errors

template<typename T>template<>T Foo<T>::bar<T>()const

error C2768: 'Foo::bar' : illegal use of explicit template arguments

+4  A: 

So there's some weird things about explicit specialisation of template members of templated classes. See this question.

One work around is to use a helper class

template< typename T, typename U>
struct FooDispatchHelper
{
   static U dispatch( const Foo<T> * f )
   {
     return f->template bar_internal<U>();
   }
};

template< typename T >
struct FooDispatchHelper<T,T>
{
   static T dispatch( const Foo<T> * f )
   {
     return f->bar_fast();
   }
};

template<typename T>class Foo
{
public:
...
   template<typename U>U bar() const
   {
      return FooDispatchHelper<T,U>::dispatch( this );
   }

  template<typename U> U bar_internal() const;
  T bar_fast() const;

};

A more complete examnple can be found here

Michael Anderson
+1: Heh, mine compiles, but doesn't link :>
Kornel Kisielewicz
Its not that weird: you can't specialize member functions without explicitly specializing their enclosing template class and you can't partially specialize functions - thus the next best general solution is forwarding calls to member functions of a partially specialized class.
Georg Fritzsche
+2  A: 

One possibility would be to use boost::enable_if / disable_if to choose which version will be available for a particular instantiation:

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

template <class T>
class Foo
{
public:
    template <class U>
    typename boost::disable_if<boost::is_same<T, U>, U>::type bar() const
    { std::cout << "Different U\n"; return U(); }

    template <class U>
    typename boost::enable_if<boost::is_same<T, U>, U>::type bar() const
    { std::cout << "Same U\n"; return U(); }
};


int main()
{
    Foo<int> f;
    f.bar<int>();
    f.bar<float>();
}
UncleBens