Search code examples
c++inheritancestatic-assert

How to static assert in a member function only if it is used?


I have the following scheme:

struct Baz {};
struct Qux {};

struct Base {
  virtual ~Base() {}
  virtual void foo() = 0;
};

template<typename T> struct Identity { static bool const value = false; };
template<typename T> void bar(T) { static_assert(Identity<T>::value, "Busted!!!"); }
template<> void bar<Baz>(Baz) {}

template<typename T>
struct Derived : Base {
  T m;
  void foo() { bar(m); }
};

int main() {
  Base *b0 = new Derived<Baz>;
  b0->foo();
  Base *b1 = new Derived<Qux>;
  (void) b1;
}

That is, I have a pure virtual class Base and a template class Derived that inherits from Base and overrides the pure virtual function foo as required. Now, inside foo I call function template bar. bar has a specialization for class Baz but not for class Qux. When in main I'm trying to materialize an object of Derived<Baz> everything's OK. But when I try to materialize an object of Derived<Qux> compiler hits static_assert.

Q:

Is there a way to transform my code in such a way that compiler will hit static assert in Derived<Qux> only if Derived<Qux>::foo() is called.

That is, materializing an object of Derived<Qux> will pass:

Base *b1 = new Derived<Qux>;

But when later in code the programmer tries to call:

b1->foo(); // compile error static assert

Solution

  • The standard says an interesting thing at [temp.inst]/9:

    An implementation shall not implicitly instantiate a function template, a variable template, a member template, a non-virtual member function, a member class, a static data member of a class template, or a substatement of a constexpr if statement, unless such instantiation is required. It is unspecified whether or not an implementation implicitly instantiates a virtual member function of a class template if the virtual member function would not otherwise be instantiated.

    The decision of instantiating a virtual function is up to the implementation, but only if it is not needed otherwise. The question we are faced with is therefore: when is the definition needed according to the standard itself?

    The answer is at [class.virtual]/11 and [temp.inst]/2:

    A virtual function declared in a class shall be defined, or declared pure in that class, or both; no diagnostic is required

    The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions, default arguments, or noexcept-specifiers of the class member functions

    So any instantiation of the class template, will instantiate a declaration of Derived::foo, which by a chain reaction requires a definition. So the definition must be instantiated too, if it is available.

    The only way an implementation can exercise the leeway it is given in the first quoted paragraph, is if Derived::foo is pure virtual too. As an example, both Clang and GCC do just that. That of course is likely to be of limited help to you.

    So to make a long story short, it's a no-starter, so long as the function is virtual (and not pure virtual).