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
.
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
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).
@StoryTeller gives a detailed answer referencing the spec, etc. but I'm going to push back on your question and ask what you are really trying to do. As the question is written, it is dead obvious the answer is "no" because you are asking for a compile time error on something that is only determinable at runtime. E.g.:
Base *b;
if (user_input() == 42) {
b = new Derived<Baz>();
} else {
b = new Derived<Qux>();
}
b->foo();
Do you want a compiler error for this case? If so you'll need to define the conditions under which you think Qux::foo
should be considered to be "called." At present the compiler is assuming a method defined as virtual in an instantiated class is called. Clearly you want something less conservative, but what?
If you have more specific compile time type information, it may be possible to catch the error at runtime. E.g.
Derived<Qux> d = new Derived<Qux>();
d->foo();
If foo
is a non-virtual templated method, it is possible it could validate the base type at compile time and then dispatch to the virtual method. (It will likely require changing the signature of foo to have the type somehow.)
A far better solution would be to break different types of functionality in the interface into different classes and introduce a mechanism to get specific interfaces on a given concrete class. This can be checked at compile time if you have the concretely types class in hand and at runtime if doing a dynamic lookup of an interface.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With