floogads
floogads

Reputation: 271

How to specialize member functions based on class template argument

What the question says. In addition, is it possible to do this inline?

Here is a small example just to give an idea...

template<typename T>
class Foo {
 public:
  Foo() :z(0.0) {}

  void do( const Foo<T> &f ) {
    z = f.z;
  }
  // specialize 'do' for Foo<int>, possible inline?

 private:
  T z;
};

Upvotes: 2

Views: 3004

Answers (3)

Johannes Schaub - litb
Johannes Schaub - litb

Reputation: 506905

You don't need to do anything complicated. Just use overloading and delegation. Note that we cannot just add an int overload, because when T turns out to be int too, this would be an invalid overload (two functions with the same signature)

template<typename T>
class Foo {
 public:
  Foo() :z(0.0) {}

  void doIt(const Foo<T> &f ) {
    doItImpl(f);
  }

 private:
  template<typename U>
  void doItImpl(const Foo<U> &f) {
    z = f.z;
  }

  void doItImpl(const Foo<int> &f) {
    /* ... */
  }

 private:
  T z;
};

Or, for this case, you can do this by specialization

template<typename T>
class Foo {
 public:
  Foo() :z(0.0) {}

  void doIt(const Foo<T> &f ) {
    z = f.z;
  }

 private:
  T z;
};

template<>
inline void Foo<int>::doIt(const Foo<int> &f) {
  /* ... */
}

Using specialization this way is only possible if all template arguments are fixed. In other words, partially specializing the member function is not possible.

Upvotes: 4

moatPylon
moatPylon

Reputation: 2191

You may try to do something like this (didn't test, might not work):

template<typename T>
class Foo {
 public:
  Foo() :z(0.0) {}

  template<typename Ty = T>
  void do( const Foo<T> &f ) {
    z = f.z;
  }

  template<>
  void do<int>( const Foo<T> &f ) {
    //specialized code
  }

 private:
  T z;
};

Upvotes: -1

James McNellis
James McNellis

Reputation: 355039

You can sort of get this behavior by making the member function a member function template and using SFINAE (substitution failure is not an error). For example:

template <typename U>
typename std::enable_if<!std::is_integral<U>::value && 
                        std::is_same<T, U>::value, void>::type
f(const Foo<U>& x)
{
}

template <typename U>
typename std::enable_if<std::is_integral<U>::value && 
                        std::is_same<T, U>::value, void>::type
f(const Foo<U>& x)
{
}

The is_integral type trait test whether U is an integer type. If it is not, the first is instantiated; if it is, the second is instantiated.

The is_same type trait tests to ensure T and U are the same type. This is used to ensure that the member function template is not instantiated for any type other than Foo<T>.

This example makes use of the C++0x <type_traits> library; Boost also has a type traits library that you can use, which works mostly the same.

Upvotes: 3

Related Questions