Ensuring a template class isn't polymorphic?

383 views Asked by At

I was recently working on a C++ library where I was designing a template class that, for efficiency and safety reasons, needed to specifically be non-polymorphic. To ensure that later on I didn't forget this and accidentally break everything, I thought I'd be a good citizen and add a static assertion to that effect.

I initially tried something like this:

template <typename T> class VirtualVerboten {
     ...

     static_assert(!std::is_polymorphic<VirtualVerboten>::value,
                   "This should not be polymorphic."); // Error!
};

This doesn't compile because, at the time that I'm using VirtualVerboten, it's an incomplete type. If this were a non-template class, I'd just put the static_assert right after the type:

class NonTemplateVirtualVerboten {
   ...
}
static_assert(!std::is_polymorphic<NonTemplateVirtualVerboten>::value,
              "This should not be polymorphic.");

But since this is a template class, the analogous idea of making a "template static_assert" isn't legal:

template <typename T> class VirtualVerboten {
     ...

};

template <typename T>              
static_assert(!std::is_polymorphic<VirtualVerboten>::value,
              "This should not be polymorphic."); // Error!

The solution I came up with was to find a member function inside of VirtualVerboten that would likely be used when the template was instantiated (specifically, the constructor), then put the static assertion in there:

template <typename T> class VirtualVerboten {
     VirtualVerboten();
};

template <typename T>
VirtualVerboten<T>::VirtualVerboten() {
  static_assert(!std::is_polymorphic<VirtualVerboten>::value,
                "This should not be polymorphic."); // Yay!
  doSomeActualThingsAtRuntime();
}

This works, except that it relies on the fact that this particular constructor will actually be invoked and therefore instantiated, which fails if there are multiple constructors that could be called.

Is there a "foolproof" way to add this static assertion here? I understand why the original code was producing an error and why you can't have a template static assertion, so this is more of a "did I miss another way of doing this?" rather than a "here's why what you did doesn't work."

2

There are 2 answers

0
iammilind On BEST ANSWER

It's already indicated by @JerryCoffin's comment. The best way is to use the static_assert in a destructor. i.e.

template <typename T> 
class VirtualVerboten {
public: 
  ~VirtualVerboten() {
    static_assert(!std::is_polymorphic<VirtualVerboten>::value,
                  "This should not be polymorphic.");
   }
};

Since destructor can be only 1, it's guaranteed that the static_assert is checked whenever there is an instantiation of its object.


IMO, another elegant way is to create a utility class & inherit the same, such as:

template<typename T>
struct NonPolymorphic
{
  ~NonPolymorphic() 
  { static_assert(!std::is_polymorphic<T>::value, "This should not be polymorphic."); }
};

template <typename T> 
class VirtualVerboten : NonPolymorphic<VirtualVerboten<T>>
{
  // ...
};
2
Indiana Kernick On

The final keyword is a C++14 feature that disallows classes from being inherited. If the class is inherited, the code will not compile. Example:

template <typename T>
class VirtualVerboten final {
  ...
}

If anyone tries to inherit it...

class Derived : public VirtualVerboten<int> {
  ...
}

The compiler will complain