-
C++ Templates
This guide will help you and provide a detail information of templates in C ++
Introduction
Templates are a feature of the C++ programming language that allows functions and classes to be generic. This allows a function or class to work on many different data types without being rewritten for each one. Thus it helps the programmers when combined with multiple inheritance and operator overloading.
Advantages
This is called a symbol that either a function, structure or a class. The interest lies in its templates:
- Generous: as long as the type parameter provides everything that is used in the symbol template, you can pass any kind
- Simplicity: it is a symbol code whatever data type is passed as a parameter, making the code much easier to maintain
Disadvantages
- As we shall see subsequently the use of template requires some precautions (type name...)
- The program takes longer to compile.
-
When using templates?
The use of templates is particularly relevant to identify containers, i.e. structures that are used to store a collection of objects (a list, a vector, a graph ...).
The templates are also adapted to define generic algorithms apply to a family of class. For example, it is interesting to encode an algorithm shortest path regardless of the structure graph. Note that the use of functions may be relevant to access weight installed on the arcs of the graph in this case. The class graph pass parameter must then verify a number of prerequisites for the
algorithm can be applied. If this is not the case the program does not compile.
What do I put in .Hpp and in .Cpp?
The C++ is a compiled language, we obviously can not imagine compiling a symbol of all its versions. For example, if we define a template class of vehicle, which I will note my_vector <T> can not imagine compile my_vector <int>, my_vector <char>, my_vector <my_struct> ... knowing that there are an infinite number of types can be passed as parameter.
That is why a class template is (re) compiled for each type of form in the program. So if I use in my program my_vector <int> and my_vector <char>, only those versions will be compiled. If another program using my_vector <my_vector <double>>, I will just compile my_vector <float> and my_vector <my_vector <float>>. The important point is that the compiler is doing just to find out what versions must be compiled.
From what has been said, it is deduced that a symbol template can be "pre-" because it is compiled for each instance. It will therefore the following rule:
If a symbol template is used only in one .Cpp (source file), it can be implemented in this .Cpp . Otherwise, it must be implemented in one .Hpp (header).
Note:
It may happen that a file containing a class template has a different extension of headers (.H or.Hpp),
for example .Tcc - This is a convention notations. Personally, I consider them as fully fledged headers.
Convention ratings
The parameters templates are generally rated with a capital (while other types are usually written in small). In practice, it
is as you want. Personally I denote preceded by a T (for example Tgraph to designate a template parameter representing a graph).
This may seem trivial but we will see that it's pretty convenient to navigate with typenames and it makes the code more readable.
Some famous templates
STL
The STL (Standard Template Library) is supplied with basic C++ compilers. This library provides a set of generic containers, including:
std:: vector: vectors (table of elements T-type adjacent to memory), access to O (1)
std:: Set: sets of T-type elements without duplication and orderly according to the operator <, access to O (log (n))
std:: list: lists (access to O (n) insertion at the beginning and end of the list O (1))
BGL
The HMO (Boost Graph Library) provides classes and generic graph algorithms that go with (algorithms shortest path algorithm afloat, rangelands graph, ...).
The latter is not present by default but easily moved. For example bug:
aptitude install libboost-graph-dev
-
First steps
To handle templates, you need four things:
-- Typename keyword: it shows that the type that follows is abstract (or template parameter depends on a parameter template) and that it should be taken into account only when it instantiates.
-- Keyword template: it indicates that the symbol (structure, class, position) who will monitor takes parameters templates. It is
written directly after the keyword template settings templates (preceded by the keyword typename, struct, class or type basis depending on the type of template parameter expected) brackets, followed by the symbol written normally. Make sure to separate the rafters (Closing) so that it should not be confused with the operator>>.
In this example we'll see
-- How to encode a class template
-- How to encode a function template
-- How to encode an operator template
In this example symbols settings to take just one parameter template, but the approach remains similar settings with several
templates.
Example:
Code:
template <typename T1, typename T2, ... >
Type_Typename_t my_function (param1_t p1, param2_t p2, ...)
{ ... }
Template <typename T1, typename T2, ... >
Class my_class_t (... );
Template <typename T1, typename T2, ... >
My_struct_t struct (... );
-- The operator::: it allows access to the fields (in particular types) and static methods of a class or a structure. It is not specific to the templates (it applies to classes and structures in general and namespaces). You can see a bit like the '/' directories. Thus std:: vector <int>:: const_iterator means that I go to type const_iterator, stored in the class <int> vector itself encoded in the namespace std.
We will define our own class of vehicle to illustrate what has been said. Of course in practice be used directly class std:: vector of the STL.
Code:
# include <iostream>
# include <cstdlib>
# include <ostream>
template <typename T>
class my_vector_t {
protected:
unsigned size;
T * data;
public:
my_vector_t (unsigned size0 = 0, const T & x0 = T( )): size (size0),
data ((T *) malloc (sizeof (T) * size0)) { for (unsigned i = 0; i <size; ++ i) data [i] = x0; }
~ my_vector_t () { free (data); }
inline unsigned size () const {return size;}
inline const T & Operator [] (unsigned i) const {
if (i> = size ())
throw: return data [i];}
inline T & operator [] (unsigned i ) {
If (i> = size ())
throw: return data [i]; } }
template <typename T>
std:: ostream & operator <<(std:: ostream & out, const my_vector_t <T> & v)
{ unsigned n = v.size ();
out << "[";
for (unsigned i = 0; i <n; ++ i)
out <<v [i] << '';
out << ']';
return out; }
template <typename T>
void write (const my_vector_t <T> & v)
{ unsigned n = v.size ();
std::cout << "[";
for (unsigned i = 0; i <n; ++ i)
std:: cout << v[i] << ' '; std:: cout << ']'; }
int main ()
{ my_vector_t <int> v(5), v [0] = 6;
v [1] = 2;
v [2] = 3;
v [3] = 4;
v [4] = 8;
write <int> (v)
std:: cout <<std :: endl;
write (v);
std:: cout <<v <<std::endl;
return 0; }
At the completion:
Code:
[6 2 3 4 8] [6 2 3 4 8] [6 2 3 4 8]
Anything that is in "early my_vector_t class" and "end my_vector_t class" could be moved to a header (eg my_vector.hpp) can be included by the main program.
-
Specifications templates
Nothing prevents specifically to implement a symbol for a set of template parameter. Note that there is no requirement to specify
all the parameters template. In this case the prototype the "less template" is privileged to remove ambiguities. If we departed the
previous example:
Code:
# include "my_vector.hpp"
void write (const my_vector_t <int> & v)
{ unsigned n = v.size ();
std::cout << "{";
for (unsigned i = 0; i <n; ++ i)
std:: cout <<v [i] << '';
std:: cout << '}'; }
int main ()
{ my_vector_t <int> v (5);
v [0] = 6;
v [1] = 2;
v [2] = 3;
v [3] = 4;
v [4] = 8;
write <int> (v);
std:: cout <<std:: endl;
write (v);
std:: cout <<std:: endl ;
std:: cout <<v <<std:: endl;
return 0; }
At the completion:
Code:
[6 2 3 4 8] (6 2 3 4 8) [6 2 3 4 8]
Template default
It is also possible to specify a template parameter default in the same manner as a parameter function.
For example:
Code:
template <typename T = int>
class my_vector_t { //... };
int main ()
{ my_vector <> v ;
return 0; }
Some famous examples of templates default in the STL function comparison, used in std:: set, is initialized by default std:: less
(function comparison based on <). So you can write indifferently:
Quote:
std:: set <int> s; std:: set <int, std:: less <int>> s_;
-
Retrieving settings templates, types and static methods of a class template
A bargain with classes templates, is making typedef (in public) to easily retrieve settings templates. Example: I have a class c1 <T>
and I want to recover the type T. This will be possible through and typedef typename.
Code:
template <typename T> typedef struct (my_class_t T data_t;);
int main () { typedef my_vector_t <int>: data_t data_t; }
However we can not implement the operator:: that if a member of the left is not an abstract data type (i.e. dependent on a type
template not yet rated). For example, if I want to manipulate the typedef "const_iterator" of class std:: vector provided by the STL,
if the parameters templates std:: vector are not affected the program will refuse to compile:
Code:
void write (const std:: vector <int> & v)
{ std:: vector <int>:: const_iterator lives (v.begin (), sells (v.end ());
for (; lives! = sells; ++ lives) std:: cout <<* lives " '';}
template <typename T>
void write (const std:: vector <int> & v)
{ std::vector <T>::const_iterator lives (v.begin(), sells(v.end()); // ERROR!
for (; lives! = sells; ++ lives) std:: cout <<* lives " '';}
Here the std:: vector <T> is located to the left of a: and depends on a parameter template. This is where typename comes in.
Code:
template <typename T>
void write (const std:: vector <int> & v)
{ typename std::vector <T>::const_iterator lives(v.begin(), sells(v.end());
for ( ; lives! = sells; ++ lives) std:: cout <<* lives " '';}
Note that when the type of a left: depends on a parameter template, it must be preceded by a typename. Given that the types quickly become heavy to handle, it is wise to make typedef. In another example, more complicated, it gives for example:
Code:
typedef typename std::vector <typename std::vector <T>::const_iterator>::const_iterator mon_type_t
Templates recursive
It is possible to define recursive templates (if so). An example:
Code:
# include <iostream>
template <int N>
int fact () { return N * fact <N-1> (); }
template <>
int fact <0> () {return 1;}
int main ()
{ std: : cout <<fact <5> () <<std:: endl; return 0; }
Here the interest is fairly moderate as we compile concrete fact <5>, fact <4> ... fact <0> is really just to give a simple example
template recursive.
What interest of a template recursive coup? Well a boost, the template allows recursive implement Tuples generic! For small curious that stands in / usr / include / boost / tuple / detail / tuple_basic.hpp, so I will not elaborate further ;-)
Tester values type template
It is possible boost to verify whether a type template is a type expected and block the compilation as appropriate. Given that it
uses the library boost, I just give a brief example:
<boost/type_traits/is_same.hpp> # include # include
<boost/static_assert.hpp> template <typename T> struct
my_struct_Test_int { BOOST_STATIC_ASSERT ((boost: is_same <T,int>: value)) / / .. . };
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
Bookmarks