Search code examples
c++templatestemplate-specializationabstractionmember-functions

How do you specialize a member function inside a template class?


Let's say I have the following class:

template <typename T>
class SomeClass : Parent<T>
{
public:
    // I have a function such as this one:
    T DoSomething(const T &t)
    {
        return t.DoSomething(some_data);
    }

    // But `T` might be a pointer, so sometimes I will need something like the following
    // instead (which obviously doesn't work as given):
    T DoSomething(const T &t)
    {
        return new T(t->DoSomething(some_data));
    }

private:
    XYZ some_data;
};

I got stuck in a giant mess of template errors trying to implement this in any semi-nice way possible using template specialization.

In the end I came up with this very ugly solution:

template <typename T>
class SomeClass : Parent<T>
{
public:
    T DoSomething(const T &x)
    {
        return Specializer<T>::Do(this, x);
    }

private:
    template <typename V>
    struct Specializer {
        static V Do(SomeClass *me, const V &x)
        {
            return x.DoSomething(me->some_data);
        }
    };

    template <typename V>
    struct Specializer<V*> {
        static V* Do(SomeClass *me, const V *&x)
        {
            return new V(x->DoSomething(me->some_data));
        }
    };

    XYZ some_data;
};

Is there a better way to do this that doesn't involve stuffing this function into a dummy class/struct and passing around my this pointer?

PS: In reality, this has nothing to do with pointers, but rather with different types of containers. Pointers were just an easy example to use here.


Solution

  • You can avoid writing any specializations, and use a type trait like std::is_pointer along with if constexpr to decide what code to execute depending on the whether the type is a pointer type or not:

    auto DoSomething(const T &t)
    {
      if constexpr (std::is_pointer_v<T>)
          return new T(t->DoSomething(some_data));
      else    
          return t.DoSomething(some_data);
    }
    

    If you don't want to check for whether T is a pointer, but want to check something else, you can still use this pattern by dropping in a suitable replacement for is_pointer.