← Go back

C++: Remove A Template Base Class At Compile Time

5 days ago by Lukas Kerkemeier

There is one pattern sometimes helpful when working with templates:

template <typename> struct Base;

template <int> struct Base<int> {...};
template <char> struct Base<char> {...};
template <bool> struct Base<bool> {...};

Now the struct Base is only defined for the types int, char and bool. The code will not compile for any other type. However the compiler error for using different types is now a linking error because the Base struct is only declared. It is possible that it is defined in a different translation unit.

The Old Trick

I often used the following trick to make a compile time error out of it:

template <typename T> struct Base {
  template<typename> struct AlwaysFalse : std::false_type{};
  static_assert(AllwaysFalse<T>::value, "Some good error message");

The value of AlwaysFalse would always be false (hence the name). However because the type depends on a template parameter, it is only visible to the compiler as a problem if the class is instantiated.

I checked this trick today against Clang-12 and it seems like clang can now see through this construct and by that it will break all code using this trick.

The New Tricks

But not all hope is lost. There are other constructs with the same result. For example you could use:

template <typename T> struct Base {
  struct NotEqualToT {};

  static constexpr bool AlwaysFalse = std::is_same_v<T, NotEqualToT>;
  static_assert(AlwaysFalse, "Some good error message");

Now AlwaysFalse is a variable directly depending on T. In theory T could be equal to NotEqualToT but that is rather unlikely (if you do not actively try to make them equal). Because of the possibility of equality the compiler should never be able to "see" through this construct.

Maybe you don't want to change a type into a value? Then there is another way. You also can rewrite the AlwaysFalse like this:

template <typename> struct AlwaysFalse {
  static constexpr bool value = false;

Currently this trick works with Clang-12 but it might break again, because the value does not depend on the template parameter.

Both tricks were checked against clang-12 and gcc-10.