C++ 18 Templates
C++ 18 Templates
Article • 10/17/2022
Templates are the basis for generic programming in C++. As a strongly-typed language,
C++ requires all variables to have a specific type, either explicitly declared by the
programmer or deduced by the compiler. However, many data structures and
algorithms look the same no matter what type they are operating on. Templates enable
you to define the operations of a class or function, and let the user specify what
concrete types those operations should work on.
C++
The above code describes a template for a generic function with a single type parameter
T, whose return value and call parameters (lhs and rhs) are all of this type. You can name
a type parameter anything you like, but by convention single upper case letters are most
commonly used. T is a template parameter; the typename keyword says that this
parameter is a placeholder for a type. When the function is called, the compiler will
replace every instance of T with the concrete type argument that is either specified by
the user or deduced by the compiler. The process in which the compiler generates a
class or function from a template is referred to as template instantiation; minimum<int> is
an instantiation of the template minimum<T> .
Elsewhere, a user can declare an instance of the template that is specialized for int.
Assume that get_a() and get_b() are functions that return an int:
C++
int a = get_a();
int b = get_b();
int i = minimum<int>(a, b);
However, because this is a function template and the compiler can deduce the type of T
from the arguments a and b, you can call it just like an ordinary function:
C++
When the compiler encounters that last statement, it generates a new function in which
every occurrence of T in the template is replaced with int :
C++
The rules for how the compiler performs type deduction in function templates are based
on the rules for ordinary functions. For more information, see Overload Resolution of
Function Template Calls.
Type parameters
In the minimum template above, note that the type parameter T is not qualified in any
way until it is used in the function call parameters, where the const and reference
qualifiers are added.
C++
The keyword class is equivalent to typename in this context. You can express the
previous example as:
C++
C++
Any built-in or user-defined type can be used as a type argument. For example, you can
use std::vector in the Standard Library to store variables of type int , double , std::string,
MyClass , const MyClass *, MyClass& , and so on. The primary restriction when using
templates is that a type argument must support any operations that are applied to the
type parameters. For example, if we call minimum using MyClass as in this example:
C++
class MyClass
{
public:
int num;
std::wstring description;
};
int main()
{
MyClass mc1 {1, L"hello"};
MyClass mc2 {2, L"goodbye"};
auto result = minimum(mc1, mc2); // Error! C2678
}
A compiler error will be generated because MyClass does not provide an overload for
the < operator.
There is no inherent requirement that the type arguments for any particular template all
belong to the same object hierarchy, although you can define a template that enforces
such a restriction. You can combine object-oriented techniques with templates; for
example, you can store a Derived* in a vector<Base*>. Note that the arguments must be
pointers
C++
vector<MyClass*> vec;
MyDerived d(3, L"back again", time(0));
vec.push_back(&d);
// or more realistically:
vector<shared_ptr<MyClass>> vec2;
vec2.push_back(make_shared<MyDerived>());
The basic requirements that std::vector and other standard library containers impose
on elements of T is that T be copy-assignable and copy-constructible.
Non-type parameters
Unlike generic types in other languages such as C# and Java, C++ templates support
non-type parameters, also called value parameters. For example, you can provide a
constant integral value to specify the length of an array, as with this example that is
similar to the std::array class in the Standard Library:
C++
Note the syntax in the template declaration. The size_t value is passed in as a template
argument at compile time and must be const or a constexpr expression. You use it like
this:
C++
Other kinds of values including pointers and references can be passed in as non-type
parameters. For example, you can pass in a pointer to a function or function object to
customize some operation inside the template code.
C++
template <auto x> constexpr auto constant = x;
C++
Because the Arr parameter itself has no body, its parameter names are not needed. In
fact, it is an error to refer to Arr's typename or class parameter names from within the
body of MyClass2 . For this reason, Arr's type parameter names can be omitted, as shown
in this example:
C++
C++
C++
vector<int> myInts;
C++
For multiple template arguments, all arguments after the first default argument must
have default arguments.
When using a template whose parameters are all defaulted, use empty angle brackets:
C++
Template specialization
In some cases, it isn't possible or desirable for a template to define exactly the same
code for any type. For example, you might wish to define a code path to be executed
only if the type argument is a pointer, or a std::wstring, or a type derived from a
particular base class. In such cases you can define a specialization of the template for
that particular type. When a user instantiates the template with that type, the compiler
uses the specialization to generate the class, and for all other types, the compiler
chooses the more general template. Specializations in which all parameters are
specialized are complete specializations. If only some of the parameters are specialized, it
is called a partial specialization.
C++
template <typename K, typename V>
class MyMap{/*...*/};
A template can have any number of specializations as long as each specialized type
parameter is unique. Only class templates may be partially specialized. All complete and
partial specializations of a template must be declared in the same namespace as the
original template.
Syntax
typename identifier ;
Remarks
The typename keyword must be used if a name in a template definition is a qualified
name that is dependent on a template argument; it's optional if the qualified name isn't
dependent. For more information, see Templates and Name Resolution.
C++
The typename keyword can also be used in place of class in template parameter lists.
For example, the following statements are semantically equivalent:
C++
Example
C++
// typename.cpp
template<class T> class X
{
typename T::Y m_y; // treat Y as a type
};
int main()
{
}
See also
Templates
Keywords
Class Templates
Article • 07/01/2022
This article describes rules that are specific to C++ class templates.
C++
// member_function_templates1.cpp
template<class T, int i> class MyStack
{
T* pStack;
T StackBuffer[i];
static const int cItems = i * sizeof(T);
public:
MyStack( void );
void push( const T item );
T& pop( void );
};
int main()
{
}
As with any template class member function, the definition of the class's constructor
member function includes the template argument list twice.
Member functions can themselves be function templates and specify extra parameters,
as in the following example.
C++
// member_templates.cpp
template<typename T>
class X
{
public:
template<typename U>
void mf(const U &u);
};
int main()
{
}
Nested class templates are declared as class templates inside the scope of the outer
class. They can be defined inside or outside of the enclosing class.
The following code demonstrates a nested class template inside an ordinary class.
C++
// nested_class_template1.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
class X
{
Y<int> yInt;
Y<char> yChar;
public:
X(int i, char c) : yInt(i), yChar(c) { }
void print()
{
cout << yInt.m_t << " " << yChar.m_t << endl;
}
};
int main()
{
X x(1, 'a');
x.print();
}
The following code uses nested template type parameters to create nested class
templates:
C++
// nested_class_template2.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
Y<int> y;
public:
X(T t) { y.Value() = t; }
void print() { y.print(); }
};
int main()
{
X<int>* xi = new X<int>(10);
X<char>* xc = new X<char>('c');
xi->print();
xc->print();
delete xi;
delete xc;
}
/* Output:
X<T>::Y<U>::Y()
X<T>::Y<U>::Y()
10
99
X<T>::Y<U>::~Y()
X<T>::Y<U>::~Y()
*/
Template friends
Class templates can have friends. A class or class template, function, or function
template can be a friend to a template class. Friends can also be specializations of a
class template or function template, but not partial specializations.
In the following example, a friend function is defined as a function template within the
class template. This code produces a version of the friend function for every
instantiation of the template. This construct is useful if your friend function depends on
the same template parameters as the class does.
C++
// template_friend1.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
public:
Array(int sz): size(sz) {
array = new T[size];
memset(array, 0, size * sizeof(T));
}
Array(const Array& a) {
size = a.size;
array = new T[size];
memcpy_s(array, a.array, sizeof(T));
}
T& operator[](int i) {
return *(array + i);
}
void print() {
for (int i = 0; i < size; i++)
cout << *(array + i) << " ";
template<class T>
friend Array<T>* combine(Array<T>& a1, Array<T>& a2);
};
template<class T>
Array<T>* combine(Array<T>& a1, Array<T>& a2) {
Array<T>* a = new Array<T>(a1.size + a2.size);
for (int i = 0; i < a1.size; i++)
(*a)[i] = *(a1.array + i);
return a;
}
int main() {
Array<char> alpha1(26);
for (int i = 0 ; i < alpha1.Length() ; i++)
alpha1[i] = 'A' + i;
alpha1.print();
Array<char> alpha2(26);
for (int i = 0 ; i < alpha2.Length() ; i++)
alpha2[i] = 'a' + i;
alpha2.print();
Array<char>*alpha3 = combine(alpha1, alpha2);
alpha3->print();
delete alpha3;
}
/* Output:
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
a b c d e f g h i j k l m n o p q r s t u v w x y z
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l
m n o p q r s t u v w x y z
*/
The next example involves a friend that has a template specialization. A function
template specialization is automatically a friend if the original function template is a
friend.
It's also possible to declare only the specialized version of the template as the friend, as
the comment before the friend declaration in the following code indicates. If you declare
a specialization as a friend, you must put the definition of the friend template
specialization outside of the template class.
C++
// template_friend2.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
int main()
{
Array<char> ac(10);
f(ac);
Array<int> a(10);
f(a);
}
/* Output:
10 generic
10 int
*/
The next example shows a friend class template declared within a class template. The
class template is then used as the template argument for the friend class. Friend class
templates must be defined outside of the class template in which they're declared. Any
specializations or partial specializations of the friend template are also friends of the
original class template.
C++
// template_friend3.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
int main()
{
Factory< X<int> > XintFactory;
X<int>* x1 = XintFactory.GetNewObject(65);
X<int>* x2 = XintFactory.GetNewObject(97);
Factory< X<char> > XcharFactory;
X<char>* x3 = XcharFactory.GetNewObject(65);
X<char>* x4 = XcharFactory.GetNewObject(97);
x1->print();
x2->print();
x3->print();
x4->print();
}
/* Output:
65
97
A
a
*/
C++
// template_specifications2.cpp
class Y
{
};
template<class T, T* pT> class X1
{
};
template<class T1, class T2 = T1> class X2
{
};
Y aY;
int main()
{
}
See also
Templates
Function Templates
Article • 08/03/2021
Class templates define a family of related classes that are based on the type arguments
passed to the class upon instantiation. Function templates are similar to class templates
but define a family of functions. With function templates, you can specify a set of
functions that are based on the same code but act on different types or classes. The
following function template swaps two items:
C++
// function_templates1.cpp
template< class T > void MySwap( T& a, T& b ) {
T c(a);
a = b;
b = c;
}
int main() {
}
This code defines a family of functions that swap the values of the arguments. From this
template, you can generate functions that will swap int and long types and also user-
defined types. MySwap will even swap classes if the class's copy constructor and
assignment operator are properly defined.
In addition, the function template will prevent you from swapping objects of different
types, because the compiler knows the types of the a and b parameters at compile time.
C++
int j = 10;
int k = 18;
CString Hello = "Hello, Windows!";
MySwap( j, k ); //OK
MySwap( j, Hello ); //error
The second MySwap call triggers a compile-time error, because the compiler cannot
generate a MySwap function with parameters of different types. If void pointers were
used, both function calls would compile correctly, but the function would not work
properly at run time.
Explicit specification of the template arguments for a function template is allowed. For
example:
C++
// function_templates2.cpp
template<class T> void f(T) {}
int main(int j) {
f<char>(j); // Generate the specialization f(char).
// If not explicitly specified, f(int) would be deduced.
}
When the template argument is explicitly specified, normal implicit conversions are
done to convert the function argument to the type of the corresponding function
template parameters. In the above example, the compiler will convert j to type char .
See also
Templates
Function Template Instantiation
Explicit Instantiation
Explicit Specialization of Function Templates
Function Template Instantiation
Article • 08/03/2021
When a function template is first called for each type, the compiler creates an
instantiation. Each instantiation is a version of the templated function specialized for the
type. This instantiation will be called every time the function is used for the type. If you
have several identical instantiations, even in different modules, only one copy of the
instantiation will end up in the executable file.
C++
// function_template_instantiation.cpp
template<class T> void f(T) { }
See also
Function Templates
Explicit instantiation
Article • 09/28/2022
Examples
This code explicitly instantiates MyStack for int variables and six items:
C++
This statement creates an instantiation of MyStack without reserving any storage for an
object. Code is generated for all members.
The next line explicitly instantiates only the constructor member function:
C++
You can explicitly instantiate function templates by using a specific type argument to
redeclare them, as shown in the example in Function template instantiation.
You can use the extern keyword to prevent the automatic instantiation of members. For
example:
C++
Similarly, you can mark specific members as being external and not instantiated:
C++
7 Note
The extern keyword in the specialization only applies to member functions defined
outside of the body of the class. Functions defined inside the class declaration are
considered inline functions and are always instantiated.
See also
Function templates
Explicit Specialization of Function
Templates
Article • 08/03/2021
With a function template, you can define special behavior for a specific type by
providing an explicit specialization (override) of the function template for that type. For
example:
C++
This declaration enables you to define a different function for double variables. Like
non-template functions, standard type conversions (such as promoting a variable of
type float to double ) are applied.
Example
C++
// explicit_specialization.cpp
template<class T> void f(T t)
{
};
See also
Function Templates
Partial ordering of function templates
(C++)
Article • 09/28/2022
Multiple function templates that match the argument list of a function call can be
available. C++ defines a partial ordering of function templates to specify which function
should be called. The ordering is partial because there can be some templates that are
considered equally specialized.
The compiler chooses the most specialized function template available from the
possible matches. For example, if a function template takes a type T and another
function template that takes T* is available, the T* version is said to be more
specialized. It's preferred over the generic T version whenever the argument is a pointer
type, even though both would be allowable matches.
Use the following process to determine if one function template candidate is more
specialized:
3. With the parameter list in T1 , see if T2 is a valid template for that parameter list.
Ignore any implicit conversions.
5. If one template is a valid template argument list for the other template, but the
converse isn't true, then that template is considered to be less specialized than the
other template. If by using the previous step, both templates form valid arguments
for each other, then they're considered to be equally specialized, and an
ambiguous call results when you attempt to use them.
a. A template specialization for a specific type is more specialized than one taking
a generic type argument.
b. A template taking only T* is more specialized than one taking only T , because
a hypothetical type X* is a valid argument for a T template argument, but X
isn't a valid argument for a T* template argument.
c. const T is more specialized than T , because const X is a valid argument for a
T template argument, but X isn't a valid argument for a const T template
argument.
Example
The following sample works as specified in the standard:
C++
// partial_ordering_of_function_templates.cpp
// compile with: /EHsc
#include <iostream>
int main() {
int i =0;
const int j = 0;
int *pi = &i;
const int *cpi = &j;
Output
Output
Less specialized function called
More specialized function called
Even more specialized function for const T*
See also
Function templates
Member function templates
Article • 09/28/2022
The term member template refers to both member function templates and nested class
templates. Member function templates are function templates that are members of a
class or class template.
Member functions can be function templates in several contexts. All functions of class
templates are generic but aren't referred to as member templates or member function
templates. If these member functions take their own template arguments, they're
considered to be member function templates.
C++
// member_function_templates.cpp
struct X
{
template <class T> void mf(T* t) {}
};
int main()
{
int i;
X* x = new X();
x->mf(&i);
}
C++
// member_function_templates2.cpp
template<typename T>
class X
{
public:
template<typename U>
void mf(const U &u)
{
}
};
int main()
{
}
// defining_member_templates_outside_class.cpp
template<typename T>
class X
{
public:
template<typename U>
void mf(const U &u);
};
int main()
{
}
Member function templates can't be virtual functions. And, they can't override virtual
functions from a base class when they're declared with the same name as a base class
virtual function.
C++
// templated_user_defined_conversions.cpp
template <class T>
struct S
{
template <class U> operator S<U>()
{
return S<U>();
}
};
int main()
{
S<int> s1;
S<long> s2 = s1; // Convert s1 using UDC and copy constructs S<long>.
}
See also
Function templates
Template Specialization (C++)
Article • 08/03/2021
Class templates can be partially specialized, and the resulting class is still a template.
Partial specialization allows template code to be partially customized for specific types in
situations, such as:
A template has multiple types and only some of them need to be specialized. The
result is a template parameterized on the remaining types.
A template has only one type, but a specialization is needed for pointer, reference,
pointer to member, or function pointer types. The specialization itself is still a
template on the type pointed to or referenced.
// partial_specialization_of_class_templates.cpp
#include <stdio.h>
struct S{};
int main() {
printf_s("PTS<S>::IsPointer == %d \nPTS<S>::IsPointerToDataMember ==
%d\n",
PTS<S>::IsPointer, PTS<S>:: IsPointerToDataMember);
printf_s("PTS<S*>::IsPointer == %d \nPTS<S*>::IsPointerToDataMember ==
%d\n"
, PTS<S*>::IsPointer, PTS<S*>:: IsPointerToDataMember);
printf_s("PTS<int S::*>::IsPointer == %d \nPTS"
"<int S::*>::IsPointerToDataMember == %d\n",
PTS<int S::*>::IsPointer, PTS<int S::*>::
IsPointerToDataMember);
}
Output
PTS<S>::IsPointer == 0
PTS<S>::IsPointerToDataMember == 0
PTS<S*>::IsPointer == 1
PTS<S*>::IsPointerToDataMember == 0
PTS<int S::*>::IsPointer == 0
PTS<int S::*>::IsPointerToDataMember == 1
C++
// partial_specialization_of_class_templates2.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
public:
Bag() : elem(0), size(0), max_size(1) {}
void add(T t) {
T* tmp;
if (size + 1 >= max_size) {
max_size *= 2;
tmp = new T [max_size];
for (int i = 0; i < size; i++)
tmp[i] = elem[i];
tmp[size++] = t;
delete[] elem;
elem = tmp;
}
else
elem[size++] = t;
}
void print() {
for (int i = 0; i < size; i++)
cout << elem[i] << " ";
cout << endl;
}
};
public:
Bag() : elem(0), size(0), max_size(1) {}
void add(T* t) {
T* tmp;
if (t == NULL) { // Check for NULL
cout << "Null pointer!" << endl;
return;
}
void print() {
for (int i = 0; i < size; i++)
cout << elem[i] << " ";
cout << endl;
}
};
int main() {
Bag<int> xi;
Bag<char> xc;
Bag<int*> xp; // Uses partial specialization for pointer types.
xi.add(10);
xi.add(9);
xi.add(8);
xi.print();
xc.add('a');
xc.add('b');
xc.add('c');
xc.print();
Output
10 9 8
a b c
Null pointer!
3 87 8 100
C++
// partial_specialization_of_class_templates3.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
void print() {
for (int i = 0; i < size; i++)
cout << "{" << keys[i] << ", " << values[i] << "}" << endl;
}
};
void sort() {
// Sort method is defined.
int smallest = 0;
for (int i = 0; i < size - 1; i++) {
for (int j = i; j < size; j++) {
if (keys[j] < keys[smallest])
smallest = j;
}
swap(keys[i], keys[smallest]);
swap(values[i], values[smallest]);
}
}
void print() {
for (int i = 0; i < size; i++)
cout << "{" << keys[i] << ", " << values[i] << "}" << endl;
}
};
int main() {
Dictionary<const char*, const char*> dict(10);
dict.print();
dict.add("apple", "fruit");
dict.add("banana", "fruit");
dict.add("dog", "animal");
dict.print();
Output
{apple, fruit}
{banana, fruit}
{dog, animal}
{100, apple}
{101, banana}
{103, dog}
{89, cat}
Sorted list:
{89, cat}
{100, apple}
{101, banana}
{103, dog}
Templates and Name Resolution
Article • 08/03/2021
Locally declared names, including the name of the template itself and any names
declared inside the template definition.
While the first two names also pertain to class and function scopes, special rules for
name resolution are required in template definitions to deal with the added complexity
of dependent names. This is because the compiler knows little about these names until
the template is instantiated, because they could be totally different types depending on
which template arguments are used. Nondependent names are looked up according to
the usual rules and at the point of definition of the template. These names, being
independent of the template arguments, are looked up once for all template
specializations. Dependent names are not looked up until the template is instantiated
and are looked up separately for each specialization.
C++
C++
T::myType
C++
N::T
A const or volatile type for which the base type is a dependent type:
C++
const T
C++
C++
C++
T<int>, MyTemplate<T>
See also
Templates
Name Resolution for Dependent Types
Article • 08/03/2021
Use typename for qualified names in template definitions to tell the compiler that the
given qualified name identifies a type. For more information, see typename.
C++
// template_name_resolution1.cpp
#include <stdio.h>
template <class T> class X
{
public:
void f(typename T::myType* mt) {}
};
class Yarg
{
public:
struct myType { };
};
int main()
{
X<Yarg> x;
x.f(new Yarg::myType());
printf("Name resolved by using typename keyword.");
}
Output
Name lookup for dependent names examines names from both the context of the
template definition—in the following example, this context would find myFunction(char)
—and the context of the template instantiation.In the following example, the template is
instantiated in main; therefore, the MyNamespace::myFunction is visible from the point of
instantiation and is picked as the better match. If MyNamespace::myFunction were
renamed, myFunction(char) would be called instead.
All names are resolved as if they were dependent names. Nevertheless, we recommend
that you use fully qualified names if there is any possible conflict.
C++
// template_name_resolution2.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
void myFunction(char)
{
cout << "Char myFunction" << endl;
}
namespace MyNamespace
{
void myFunction(int)
{
cout << "Int MyNamespace::myFunction" << endl;
}
};
int main()
{
Class1<int>* c1 = new Class1<int>(100);
}
Output
Output
Int MyNamespace::myFunction
Template Disambiguation
Visual Studio 2012 enforces the C++98/03/11 standard rules for disambiguation with
the "template" keyword. In the following example, Visual Studio 2010 would accept both
the nonconforming lines and the conforming lines. Visual Studio 2012 accepts only the
conforming lines.
C++
#include <iostream>
#include <ostream>
#include <typeinfo>
using namespace std;
int main() {
cout << typeid(Container<int, Allocator<float>>::AX).name() << endl;
}
See also
Name Resolution
Name Resolution for Locally Declared
Names
Article • 09/28/2022
The template's name itself can be referred to with or without the template arguments. In
the scope of a class template, the name itself refers to the template. In the scope of a
template specialization or partial specialization, the name alone refers to the
specialization or partial specialization. Other specializations or partial specializations of
the template can also be referenced, with the appropriate template arguments.
C++
// template_name_resolution3.cpp
// compile with: /c
template <class T> class A {
A* a1; // A refers to A<T>
A<int>* a2; // A<int> refers to a specialization of A.
A<T*>* a3; // A<T*> refers to the partial specialization A<T*>.
};
C++
// template_name_resolution4.cpp
// compile with: /EHsc
template <class T>
class Base1 {};
int main() {
// Derived1<int> d;
}
C++
// template_name_resolution5.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
int main() {
C<int> c;
c.f();
}
Output
Z::Z()
C++
// template_name_resolution6.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
namespace NS {
void g() { cout << "NS::g" << endl; }
int main() {
NS::C<int> c;
c.f();
}
Output
C<T>::g
C++
// template_name_resolution7.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
struct B {
int i;
void print() { cout << "Base" << endl; }
};
int main() {
C<int, 1> c;
c.f();
cout << c.i << endl;
}
Output
Base
1
See also
Name resolution
Overload resolution of function
template calls
Article • 09/28/2022
A function template can overload non-template functions of the same name. In this
scenario, the compiler first attempts to resolve a function call by using template
argument deduction to instantiate the function template with a unique specialization. If
template argument deduction fails, then the compiler considers both instantiated
function template overloads and non-template function overloads to resolve the call.
These other overloads are known as the candidate set. If template argument deduction
succeeds, then the generated function is compared with the other functions in the
candidate set to determine the best match, following the rules for overload resolution.
For more information, see Function overloading.
C++
// template_name_resolution9.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
int main()
{
f(1, 1); // Equally good match; choose the non-template function.
f('a', 1); // Chooses the function template.
f<int, int>(2, 2); // Template arguments explicitly specified.
}
Output
f(int, int)
void f(T1, T2)
void f(T1, T2)
C++
// template_name_resolution10.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
int main()
{
long l = 0;
int i = 0;
// Call the function template f(long, int) because f(int, int)
// would require a conversion from long to int.
f(l, i);
}
Output
See also
Name resolution
typename
Source code organization (C++
Templates)
Article • 07/01/2022
When defining a class template, you must organize the source code in such a way that
the member definitions are visible to the compiler when it needs them. You have the
choice of using the inclusion model or the explicit instantiation model. In the inclusion
model, you include the member definitions in every file that uses a template. This
approach is simplest and provides maximum flexibility in terms of what concrete types
can be used with your template. Its disadvantage is that it can increase compilation
times. The times can be significant if a project or the included files themselves are large.
With the explicit instantiation approach, the template itself instantiates concrete classes
or class members for specific types. This approach can speed up compilation times, but
it limits usage to only those classes that the template implementer has enabled ahead of
time. In general, we recommend that you use the inclusion model unless the
compilation times become a problem.
Background
Templates aren't like ordinary classes in the sense that the compiler doesn't generate
object code for a template or any of its members. There's nothing to generate until the
template is instantiated with concrete types. When the compiler encounters a template
instantiation such as MyClass<int> mc; and no class with that signature exists yet, it
generates a new class. It also attempts to generate code for any member functions that
are used. If those definitions are in a file that isn't #included, directly or indirectly, in the
.cpp file that is being compiled, the compiler can't see them. From the compiler's point
of view, it's not necessarily an error. The functions may be defined in another translation
unit where the linker will find them. If the linker doesn't find that code, it raises an
unresolved external error.
C++
#ifndef MYARRAY
#define MYARRAY
#include <iostream>
T& operator[](int i)
{
return arr[i];
}
};
#endif
With this approach, the compiler has access to the complete template definition and can
instantiate templates on-demand for any type. It's simple and relatively easy to
maintain. However, the inclusion model does have a cost in terms of compilation times.
This cost can be significant in large programs, especially if the template header itself
#includes other headers. Every .cpp file that #includes the header will get its own copy
of the function templates and all the definitions. The linker will generally be able to sort
things out so that you don't end up with multiple definitions for a function, but it takes
time to do this work. In smaller programs that extra compilation time is probably not
significant.
You create an explicit instantiation by using the keyword template followed by the
signature of the entity you want to instantiate. This entity can be a type or a member. If
you explicitly instantiate a type, all members are instantiated.
C++
//MyArray.h
#ifndef MYARRAY
#define MYARRAY
The source file MyArray.cpp explicitly instantiates template MyArray<double, 5> and
template MyArray<string, 5> :
C++
//MyArray.cpp
#include <iostream>
#include "MyArray.h"
In the previous example, the explicit instantiations are at the bottom of the .cpp file. A
MyArray may be used only for double or String types.
7 Note