Can I ask the compiler to forbid defining a generic version of a member function of a templated class?

Viewed 73

Suppose I have an interface I, and a templated class C which implements the interface:

class I {
public:
  virtual int foo() = 0;
};

template<typename T>
class C : public I {
public:
  int foo() override;
};

I want there to be only two instantiations of C, with types T1 and T2. I want to specialize foo() for each of them. I can do this in my .cpp file like this:

template<>
C<T1>::foo() { return 1; }

template<>
C<T2>::foo() { return 2; }

template class C<T1>;
template class C<T2>;

I am wary that someone will come along with more template parameters they want to use for C, such as T3 and T4, and will define a generic foo()

template<typename T>
C::foo() { return 0; }

// (with appropriate specialization declarations)
template class C<T3>;
template class C<T4>;

because then, in a final scenario, someone could add a fifth type and forget to specialize foo() for it:

// forgotten:
// template<>
// C::<T5>::foo() { return 5; }

template class C<T5>;

How can I ask the compiler to complain about someone adding the generic foo(), either in a unit test or in the implementation of C?

2 Answers

You should add the generic definition yourself:

template<typename T>
int C<T>::foo() { static_assert(!std::is_same_v<T,T>, "Missing specialization of C<T>::foo()!"); return 0; }

Since the condition for the static_assert is always false, everyone trying to use C with a not yet specialized type will get the static assertion error (which will additionally provide a useful hint as to how to solve this problem).

Because you already provided a definition for C<T>::foo() anyone trying to provide his own generic definition of C<T>::foo() will get a compiler error because of redefinition.

Note that the condition inside the static_assert must be dependent on the template type to only trigger the assertion upon template instantiation.

If you accept only two template types for T... what about a static_assert()

template<typename T>
class C : public I {

   static_assert(    std::is_same<T, T1>::value
                  or std::is_same<T, T2>::value, "bad parameter");

public:
  int foo() override;
};
Related