Templates – C++ templates explained



Templates – C++ templates explained

0 0


Templates

A presentation about C++ templates.

On Github mg6 / Templates

Templates

C++ templates explained

M. Gamrat & B. Kordalski

Types

  • function templates
  • class templates

Function templates

sum(x, y)

Let's have a function that returns a sum of 2 numbers.

Multiple type-specific implementations

float sum(float a, float b)
{
    return a + b;
}
          
int sum(int a, int b)
{
    return a + b;
}
          
char sum(char a, char b)
{
    return a + b;
}
          

The code is literally the same. How about we combine it?

Templated version!

template <class T>
T sum(T a, T b)
{
    return a + b;
}

// usage:
int     result1 = sum(4, -5);
double  result2 = sum(5.6, 2.4);
char    result3 = sum('.', ',');  // equals 'Z'
          

Single declaration - 3 resultant functions - explain class keyword - type doesn't have to necessarily be of class type

template <class T>
T sum(T a, T b) { return a + b; }

sum(5.6, 2.4)
          

5.6 → double   2.4 → double
          

double sum(double a, double b) { return a + b; }
          

The compiler deduces proper type on function call.

important statement - compiler creates functions of only needed/used types

Template syntax

template <class T> /* templated function declaration here */
          

such as:

/* square(x) -> return x^2 */

template <class T> T square(T arg) { return arg * arg; }
          

syntax used also for classes

Multiple types

We can declare multiple type parameters as well:

template <class A, class B, class C> /* declaration here */
          

variable-length type lists also possible

(template<...> class list)

Parameter naming

Type names A, B, C are arbitrary.

Obey variable naming conventions!

greater(x, y)

So far, so good. Now let's write a function returning greater value of 2 given.

Do we need a template here?

Of course not.

#define to the rescue!

#define greater(x, y) ((x) > (y) ? (x) : (y))

// usage:
float pi = 3.14f;
float e = 2.71f;

cout << greater(pi, e); // pi wins here
            

Wait for the answer

Why our macro suc fails?

There is no type safety.

char* title = "hello there";
char* descr = "a description of a book";

greater(title, descr) // complete nonsense compiles easily!
            

Macro functions are just simple text replacements.

Wait for answer!

Templated comparison

template <class T>
T greater(T a, T b)
{
    if (a > b)
        return a;

    return b;
}

// usage:
int x1 = 5;
int x2 = 3;
cout << greater(x1, x2);
          

What if we had big objects to compare instead of int?

Use references!

template <class T>
T& greater(T& a, T& b)
{
    if (a > b)
        return a;

    return b;
}

// since we return a reference now,
// it's possible to do the following:

int x1 = 5;
int x2 = 3;

greater(x1, x2) = -7;

// which at runtime is an equivalent of:

x1 = -7;
          

sum_array(x, n)

Now define a function that counts the sum of given array of elements.

Obviously we need the array x and its size n.

Integer case

int sum_array(int data[], int count)
{
    int sum = 0;

    for (int i = 0; i < count; ++i)
      sum += data[i];

    return sum;
}
          

Templated array sum

template <class T>
T sum_array(T data[], int count)
{
    T sum = T();   // why not sum = 0?

    for (int i = 0; i < count; ++i)
        sum += data[i];   // watch out!

    return sum;
}

// usage:
int int_array[4]    = { 1, 6, 2, 1 };
char char_array[3]  = { 'a', 'c', 'b' };
std::string text[2] = { "test", "ing" };

sum_array(int_array, 4)   // == int(10)
sum_array(char_array, 3)  // == char('&')
sum_array(text, 2)        // == std::string("testing")

          

Explain differences - why T()? - assumption for operator+=

Class templates

Point class

Let's have a 2D point structure.

struct Point
{
    int x;
    int y;
}

// usage:
Point p = {-2, 9};
          

Podstawy, proste, nic nowego

Adjustable precision?

template <class T>
struct Point
{
    T x;
    T y;

    Point() : x(T()), y(T()) {}
    Point(T _x, T _y) : x(_x), y(_y) {}
    Point(const Point& p) : x(p.x), y(p.y) {}
}

// usage:
Point<float> p(45.5f, 251.3f);
          
  • T - TYP podany przez deva
  • konstruktory - Pierwszy nie wiemy jaki „typ” mamy.
  • Copy constructor

Declaring templated objects

If we write:

Point<float> p;
          

we tell the compiler to replace all Ts in class template with float at compile time, so that the constructor can accept float parameters.

This is called the instantiation of class template.

That's the time the compiler checks for errors.

Hold on.

Are Point<float> and Point<unsigned> same classes?

No, they represent two different classes for the compiler.

Zaczekaj na odpowiedź z sali.

Point class would be useful. But we can do better.

Pair class

Let's write a general purpose Pair class.

W ten sposób będziemy mieli klasę uniwersalną, która przechowa nie tylko punkty

Template class Pair

template <class F, class S>
struct Pair
{
    F first;
    S second;

    Pair() : first( F() ), second( S() ) {}
    Pair(F _first, S _second) : first(_first), second(_second) {}
    Pair(const Pair& p) : first(p.first), second(p.second) {}
}

// usage:
Pair<int, int> point(3, 5);
Pair<std::string, float> product_rating;

// or even:
typedef Pair<float, float> Point;
Point another(25.0f, -33.0f);  // explicit values given
Point yet_another(another);    // copy from another
          

We can pass 2 different types to our class now.

  • „1st, 2nd” mogą być różne typy nazwane F i S.
  • Typedef - skracamy sobie nazwę typu (readability purposes)
  • T - TYP podany przez deva
  • konstruktory - Pierwszy nie wiemy jaki „typ” mamy.
  • Copy constructor

Data holder class

Finally let's write a simple getter/setter class.

Data holder class

template <class T>
class DataHolder
{
private:
    T data;

public:
    DataHolder() : data( T() ) {}
    DataHolder(T value) : data(value) {}

    void set(T value)
    {
        data = value;
    }

    T get()
    {
        return data;
    }
}
          

Wytłumacz set get

Data holder in action

DataHolder<double> mass;
mass.set(56.63);
cout << mass.get() << endl;
          

THE END

M. Gamrat & B. Kordalski, 2014

Check out a great guide on C++ templates used for this presentation.

Task for you!

Write the template class Vector3d with:
  • private x,y,z coordinates
  • default constructor (setting default values) and destructor
  • constructor accepting x,y,z parameters
  • copy constructor Vector3d(const Vector3d& vec)
  • setter set(x,y,z) for all coords at once
  • setters and getters for single coords
  • getLength() and normalize() methods
Define main() function that:
  • declares a fixed int 3D vector and display its values
  • has a float vector with data provided by the user
  • normalize user vector and present it to the user
  • check the length of selected vector
Aid yourself with slides: mg6.github.com/Templates

operator+, operator-