Search code examples
c++templatesc++11explicit-instantiation

Separating definition/instantiation of template classes without 'extern'


The (not so new anymore) C++11 standard introduced the extern keyword for templates. Its purpose is to tell the compiler that a template should not be instantiated at the point of usage, but that it will be instantiated in another translation unit (and thus there will be an instantiation available at link time) - at least AFAIK.

Now, even in the pre-C++11 era we used something similar to separate the declaration/definition of template classes from its instantiation in order to speed up compilation, e.g. like so:

point.h: class definition

template <int dim> struct Point {
  ...
  void foo();
  ...
};

point.cpp: method definitions

#include "point.h"
template <int dim>
void Point<dim>::foo() {
  ...
}

point_2d.cpp: class instantiation (2D version)

#include "point.cpp"
template struct Point<2>;

point_3d.cpp: class instantiation (3D version)

#include "point.cpp"
template struct Point<3>;

main.cpp: usage of 2D and 3D points

#include "point.h"
int main(int, char**) {
    Point<2> p;
    p.foo();
}

Now I am wondering:

  • Is our approach valid C++ (03 or 11) code or are we just lucky it worked?
  • With C++11, would we be able to achieve the same thing by including point.cpp in main.cpp and declaring extern template <int dim> struct Point;?

Solution

  • Your approach is a valid C++ code and should work both in C++03 and C++11. It is called explicit instantiation:

    template struct Point<2>;
    

    While applying to class templates it explicitly instantiates all members of a class template for some template argument list in the current translation unit. With this approach it is simple to create the libraries of templates for which the set of possible template arguments is known in advance (as in your case when Point can be 2D and 3D but not 1D, 4D etc.).

    In C++11 when you add to explicit instantiation directive keyword extern:

    extern template struct Point<2>;
    

    it becomes a declaration, not a definition. The behavior of such thing is similar to usual extern keyword for variables. Explicit template instantiation declaration can be used together with explicit instantiation in the following manner:

    // point.h
    
    template <int dim> struct Point {
      ...
      void foo();
      ...
    };
    
    extern template struct Point<2>;
    extern template struct Point<3>;
    
    #include "point.hpp"
    
    
    // point.hpp
    
    #include "point.h"
    
    template <int dim>
    void Point<dim>::foo() {
      ...
    }
    
    // point.cpp
    
    #include "point.hpp"
    
    template struct Point<2>;
    template struct Point<3>;
    

    With such code you achieve the same result as with your one but additionally you allow the users of your code to use Point<1>, Point<4> and other specializations of a Point class template if they want. Without extern template struct Point<2>; and extern template struct Point<3>; directives Point class template would be implicitly instantiated in the users code even for template arguments 2 and 3 that reduces the meaning of explicit instantiation (template struct Point<2>; and template struct Point<2>;).