Search code examples
c++templatesclass-templatetemplate-inheritance

How to restrict a class template parameter to a certain subclass?


This is what I am trying:

C is a template parameter that is either SomeClass or SomeDerivedClass:


class SomeClass
{
protected:
  int ProtectedBaseClassMember;
  virtual void SomeFunctionFromBaseClass();
};

class SomeDerivedClass : public SomeClass { };

How to restrict C to subclasses of SomeClass?

template<class C>
class SmuggleInBetween : public C
{
public:
  SmuggleInBetween()
  {
    // does not compile: unknown symbol "ProtectedBaseClassMember"
    ProtectedBaseClassMember = 5;
 
    // does compile, but ...
    SomeClass* Me = static_cast<SomeClass>(this);
    // ... member not accessible:
    Me->ProtectedBaseClassMember = 5;
  }
protected:
  // doesn't compile: "method with override specifier did not override any base class method"
  virtual void SomeFunctionFromBaseClass() override;
  double DoubleProperty;
};

I found the very related question: Restrict C++ Template parameter to subclass, but over there the author seems fine just restricting the template parameter w/o ever accessing any symbols from the base class.

The use case for the templated SmuggleInBetween class is this: I can create

class SomeImprovedClass : public SmuggleInBetween<SomeClass> { };

and

class SomeImprovedDerivedClass : public SmuggleInBetween<SomeDerivedClass> { };

without having to duplicate the code that I smuggled in via this pattern (if possible).

Btw, this happens in the context of Unreal Engine. It might be that a solution that is possible in C++ still causes additional headache with the Unreal Header Tool that sometimes doesn't tolerate things that wander too far off the known paths.


Solution

  • This is one way to do it:

    template<class C>
    class SmuggleInBetween : public C
    {
        static_assert(std::is_base_of_v<SomeClass, C>, "C must be a descendant of SomeClass");
        /* ... */
    };
    

    Of course, in general you could also use std::enable_if_v<std::is_base_of_v<SomeClass, C>, ...> if you need SFINAE; here I think static_assert() does the trick.