Template Instantiation
Template instantiation is creating a concrete function or a concrete class out of a function or class template. Creating template instantiation can be implicit (compiler-generated) or explicit (user-provided).
When you need a template for a specific argument, the compiler auto-generates it. Sometimes, you want to remove template definitions from header files or 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 the compiler automatically generates the concrete function or class for the provided template arguments. 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'; }
Lines (1) and (2) use class template argument deduction (CTAG). std::vector
or MyClass
can deduce its type from their constructor arguments. Line (3) also deduces its template argument. In line (4), on the contrary, the template argument double
is explicitly specified: isSmaller<double>(5.5f, 6.5
).
The compiler creates a concrete function or class for each implicit template instantiation. C++Insights visualizes this process.
This automatic process is very comfortable but has a few drawbacks.
Modernes C++ Mentoring
Do you want to stay informed: Subscribe.
- When you implicitly instantiate a template, its definition is typically visible in a header file. However, you may not want to disclose the definition.
- When you need a template for specific template arguments, the compiler instantiates if unavailable in the concrete translation unit. A translation unit is the source file after processing 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'; }
Lines (1) to (6) are the interesting ones. Thanks to the keyword template
, explicit template
instantiation happens.
-
Line (1) explicitly instantiated
std::vector
forint
and line (2) its member functionempty
fordouble.
-
Line (3) explicitly instantiates
MyClass
forint
and line (4) its member functiongetType
fordouble
. -
Line (5) explicitly instantiated
isSmaller
for(int, int)
, and line (6) does the same for(double, double)
providing the explicit template argumentdouble
.
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.
But when I try to use MyClass
another type then int
, I get a linker error. I get this linker error message when I use the commented-out lines.
There is no template instantiation double
available.
Suppress the Template Instantiation
Assume you use MyClass<int
> in various translation units that 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 will write in my next post about variadic templates … .
Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Stephen Kelley, Kyle Dean, Tusar Palauri, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, Bhavith C Achar, Marco Parri Empoli, Philipp Lenk, Charles-Jianye Chen, Keith Jeffery, Matt Godbolt, and Honey Sukesan.
Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.
My special thanks to Embarcadero | |
My special thanks to PVS-Studio | |
My special thanks to Tipi.build | |
My special thanks to Take Up Code | |
My special thanks to SHAVEDYAKS |
Modernes C++ GmbH
Modernes C++ Mentoring (English)
Rainer Grimm
Yalovastraße 20
72108 Rottenburg
Mail: schulung@ModernesCpp.de
Mentoring: www.ModernesCpp.org
Modernes C++ Mentoring,
Leave a Reply
Want to join the discussion?Feel free to contribute!