16
class P
{
};

template< typename P >
class C : public P
{
  public:
  void f()
  {
    P::f();
  }
};

int main() {
  C<P> c1;
  return 0;
}

Just in case my question leaves any room for misunderstanding, here is a code example. If C was not templated but inherited from P directly, then the sample would fail to compile because clearly function f() attempts to call a function on base class P which is non-existent.

However if C is templated then this is only picked up if f() is actually called.

I'd like to know why there is this difference. In both instances f() would be dead code and stripped anyway, yet the program is ill-formed in the non-template scenario.

Baum mit Augen
  • 49,044
  • 25
  • 144
  • 182
qeadz
  • 1,476
  • 1
  • 9
  • 17

5 Answers5

20

Actually, the program you posted is not ill-formed. While the "implicit instantiation" C<P> c1; instantiates all member declarations,

the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist

[N4140, 14.7.1(2)] Since you never use C<P>::f, its definition is never needed and thus never instantiated.

This is different to an "explicit instantiation" like

template class C<P>;

which would instantiate the definition of all members of C<P> and thus result in an error.

Baum mit Augen
  • 49,044
  • 25
  • 144
  • 182
8

Because it makes sense to allow to use only the functions that are actually well formed.

Take vector::push_back(const T&), that requires T to be copyable. But you can still use most of vector even if T is a movable non-copyable type.

sbabbi
  • 11,070
  • 2
  • 29
  • 57
5

Inside the template class, identifier P is a template parameter typename, it does not refer to the class P defined before. So you can't say P::f() is not-existent, as you do not know what class will be substituted for parameter P.

And later, when you invoke the template to declare the c1 variable, the function is not needed, so there is no 'dead code' – there is just a template, which has never been expanded to an actual code.

CiaPan
  • 9,381
  • 2
  • 21
  • 35
3

If I write a non-template class which inherits from P, calling a non-existent function on that class from a member function is obviously an error as there is no use for it whatsoever.

If C is a template class, however, maybe that erroneous call is valid for some other P. Maybe that member function represents some extended functionality for types which support some given interface. C could be instantiated from some external library that I have no knowledge of when instantiating for P. As such, throwing a compiler error for an unused function in a template class which could be valid for certain types would limit the expressive power of templates. It also brings down compile-times, binary sizes, etc. because that unneeded template function doesn't need to be generated for types when it is not used.

TartanLlama
  • 63,752
  • 13
  • 157
  • 193
2

Variable C<P> c1; is not used and the C::f() function is never called. Compiler doesn't create body of function until it is used. This is a simple optimization which speeds up the compilation.

Piotr Siupa
  • 3,929
  • 2
  • 29
  • 65