Template Instantiation

Contents[Show]

Template instantiation is the creation of a concrete function or a concrete class out of a function template or class template. The creation of template instantiation can be implicit (compiler-generated) or explicit (user-provided).

 templatesInstantiation

When you need a template for a specific template argument, the compiler auto-generates it for you. Sometimes, you want to remove template definitions from header files or you want to avoid compute-power-consuming template instantiation. In this case, explicit instantiation is your friend.

Implicit Instantiation

Implicit instantiation should be your default choice. Implicit instantiation means that the compiler automatically generates the concrete function or class for the provided template arguments. In general, the compiler also deduces the template arguments from the function's arguments. In C++17, the compiler can also deduce the template arguments for class templates.

 

// implicitTemplateInstantiation.cpp

#include <iostream>
#include <string>
#include <vector>

template <typename T>
class MyClass{
 public:
    MyClass(T t) { }
    std::string getType() const {
        return typeid(T).name();
    }
};

template<typename T>
bool isSmaller(T fir, T sec){
    return fir < sec;
}

int main(){

    std::cout << '\n';

    std::cout << std::boolalpha;
  
    std::vector vec{1, 2, 3, 4, 5};          // (1)
    std::cout << "vec.size(): " << vec.size() << '\n';
  
    MyClass myClass(5);                      // (2)
    std::cout << "myClass.getType(): " << myClass.getType() << '\n';
  
    std::cout << '\n';
  
    std::cout << "isSmaller(5, 10): " 
              << isSmaller(5, 10) << '\n';   // (3)
    std::cout << "isSmaller<double>(5.5f, 6.5): " 
              << isSmaller<double>(5.5f, 6.5) << '\n';    // (4)
  
    std::cout << '\n';
  
}

 

Line (1) and (2) use class template argument deduction (CTAG). std::vector or MyClass can deduce its type from their constructor arguments. Line (3) deduces also its template argument. In line (4) on the contrary, the template argument double is explicitly specified: isSmaller<double>(5.5f, 6.5).

implicitTemplateInstantiation

The compiler creates for each implicit template instantiation a concrete function or class. C++Insights visualizes this process.

This automatic process is very comfortable, but has a few drawbacks.

  1. When you implicit instantiate a template, the definition of the template is typically visible in a header file. Maybe, you don't want to disclose the definition.
  2. When you need a template for specific template arguments, the compiler instantiates if it is not available in the concrete translation unit. A translation unit is the source file after processing of the C preprocessor. Typically, the linker removes all redundant template instantiations and keeps one. This is a waste of time and space.

Both issues can be solved with explicit template instantiation.

Explicit Instantiation

Explicit instantiation has two flavors. Explicit instantiation definition and explicit instantiation declaration.

  • Explicit instantiation definition syntax: template <template declaration>
  • Explicit instantiation declaration syntax: extern template <template declaration>

When you study the syntax, the keyword extern makes the difference.

Explicit template instantiation means that you generate the definition of a template. Here is a simple example.

// explicitTemplateInstantiation.cpp

#include <iostream>
#include <string>
#include <vector>

template <typename T>
class MyClass{
 public:
    MyClass(T t) { }
    std::string getType() const {
        return typeid(T).name();
    }
};

template<typename T>
bool isSmaller(T fir, T sec){
  return fir < sec;
}
 
template class std::vector<int>;                       // (1)
template bool std::vector<double>::empty() const;      // (2)

template class MyClass<int>;                           // (3)
template std::string MyClass<double>::getType() const; // (4)

template bool isSmaller(int, int);                     // (5)
template bool isSmaller<double>(double, double);       // (6)

int main(){

  std::cout << '\n';
  
  std::cout << std::boolalpha;
  
  std::vector vec{1, 2, 3, 4, 5};
  std::cout << "vec.size(): " << vec.size() << '\n';
  
  MyClass myClass(5);
  std::cout << "myClass.getType(): " << myClass.getType() << '\n';
  
  std::cout << '\n';
  
  std::cout << "isSmaller(5, 10): " 
            << isSmaller(5,10) << '\n';
  std::cout << "isSmaller<double>(5.5f, 6.5): " 
            << isSmaller<double>(5.5f, 6.5) << '\n';
  
  std::cout << '\n';
  
}

 

The lines (1) to (6) are the interesting ones. Thanks to the keyword template, explicit template instantiation happens.

  • Line (1) explicitly instantiated std::vector for int and line (2) its member function empty for double.
  • Line (3) explicitly instantiates MyClass for int and line (4) its member function getType for double.
  • Line (5) explicitly instantiated isSmaller for (int, int), and line (6) does the same for (double, double) providing the explicit template argument double.

Hide the Template Implementation

How can explicit template instantiation help you hide the definition of the templates?

  • Put the template declaration in the header file.
  • Put the template definition in the source file. Explicitly instantiate the template at the end of the source file.
  • Use the template by including the header file.

Here are three files exemplifying this process.

  • Template declaration
// MyClass.h

#include <typeinfo>
#include <string>

template <typename T>
class MyClass{
 public:
    MyClass(T t) { }
    std::string getType() const;
};

 

  • Template definition and explicit instantiation for int
// MyClass.cpp

#include "MyClass.h"

template <typename T>
std::string MyClass<T>::getType() const {
    return typeid(T).name();
}

template class MyClass<int>; 

 

  • Template use
// mainMyClass.cpp

#include "MyClass.h"
#include <iostream>

int main() {

    std::cout << '\n'; 

    MyClass myClass(5);
    std::cout << "myClass.getType(): " << myClass.getType() << '\n';

    /*
    MyClass myClass2(5.5);
    std::cout << "myClass2.getType(): " << myClass2.getType() << '\n';
    */

    std::cout << '\n';

}

 

Compiling and running the program gives the expected result.

mainMyClass

But when I try to use MyClass for another type than int, I get a linker error. This is the linker error message I get when I use the commented-out lines.

mainMyClassError There is no template instantiation for double available.

Suppress the Template Instantiation

Assume you use MyClass<int> in various translation units which the linker puts together. Essentially, the linker throws away all template instantiations but one. This is a waste of computing time. Thanks to the use of the extern keyword in C++11, you can make out of an explicit template instantiation definition an explicit template instantiation declaration. What?

template class MyClass<int>;        // explicit instantiation definition
extern template class MyClass<int>; // explicit instantiation declaration 

 

The key observation is that the second line does not cause a template instantiation. This means that the compiler generates not something the linker throws away. You have only to ensure that one instantiation of MyClass<int> is for the linker available. If not, you will get a linker error. 

What's next?

After this more technical post, I write in my next post about variadic templates ... .

 

Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, Marko, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Animus24, Jozo Leko, John Breland, espkk, Louis St-Amour, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Neil Wang, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Tobi Heideman, Daniel Hufschläger, Red Trip, Alexander Schwarz, Tornike Porchxidze, Alessandro Pezzato, Evangelos Denaxas, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Dimitrov Tsvetomir, Leo Goodstadt, Eduardo Velasquez, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, and Robin Furness.

 

Thanks in particular to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, and Said Mert Turkal.

 

 

My special thanks to Embarcadero CBUIDER STUDIO FINAL ICONS 1024 Small

 

Seminars

I'm happy to give online seminars or face-to-face seminars worldwide. Please call me if you have any questions.

Bookable (Online)

German

Standard Seminars (English/German)

Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.

New

Contact Me

Modernes C++,

RainerGrimmSmall

 

Comments   

0 #1 anonymous 2021-08-02 18:20
if you're testin and have a single file, you can just

$ make mainMyClass
Quote
0 #2 juhani 2021-08-14 11:08
Quoting anonymous:

$ make mainMyClass


That's assuming `make` is installed, though I have never seen a non-Windows system without it. It echoes the recipe, so should be fine for education as well.

Btw, I would change "generates not something" to "won't generate something", for I think that "not" there binds to "something" instead of "generates", suggesting that prepending "extern" would make the compiler generate something that the linker won't throw away :p
Quote

My Newest E-Books

Course: Modern C++ Concurrency in Practice

Course: C++ Standard Library including C++14 & C++17

Course: Embedded Programming with Modern C++

Course: Generic Programming (Templates)

Course: C++ Fundamentals for Professionals

Interactive Course: The All-in-One Guide to C++20

Subscribe to the newsletter (+ pdf bundle)

Blog archive

Source Code

Visitors

Today 257

Yesterday 8036

Week 16472

Month 191182

All 7253472

Currently are 194 guests and no members online

Kubik-Rubik Joomla! Extensions

Latest comments