Szablon jest ze swej natury uogólnieniem – opisuje grupę funkcji lub klas w ramach pewnych pojęć ogólnych. Jeżeli zostanie przekazany typ jako parametr szablonu (jawnie lub niejawnie przez dedukcję na podstawie argumentów) kompilator wygeneruje odpowiedni kod będący specjalizacją szablonu. Wygenerowany kod uważany jest za instancję szablonu.

Zaletą stosowania szablonów jest unikanie ręcznego powielania kodu, całą pracę wykonuje kompilator. W celu ograniczenia generowania kodu dla typów wskaźnikowych można zastosować specjalizację pełną i częściową.

Szablony funkcji

Podczas tworzenia szablonu nie trzeba stosować nawiasów kątowych (< >) i podawać wszystkich argumentów szablonu. Kompilator na podstawie przekazanych argumentów funkcji wywnioskowuje wartości. Nie powiedzie się jeżeli dla jednego parametru szablonu zostaną przekazane dwa różne typy wartości np. w funkcji dwuargumentowej.

Niedopuszczalne są domyślne argumenty funkcji.

W przypadku szablony, który zawiera dwa parametry z czego jeden jest zwracany konieczne jest podanie jako pierwszy parametr. W innym przypadku nie powiedzie się kompilacja, dopóki nie będzie znany typ wartości zwracanej.

Specjalizacja jawna
Dla następującego szablonu:

template <class T> void f(T t) {}

specjalizacja dla typu int zdefiniowana jest następująco:

template<> void f<int>(int n) {}

template<> jest informacją dla kompilatora, że dalej znajduje się specjalizacja szablonu. Typ specjalizacji musi znajdować się za nazwą funkcji.

Specjalizacja częściowa
W specjalizacji częściowej nie wszystkie parametry są określone.

Podczas ukonkretniania szablonu kompilator generuje tylko kod, który jest używany.

class X
{
public:
  void f() {}
};

class Y
{
public:
  void g() {}
};

template <typename T> class Z
{
  T t;

public:
  void a() { t.f(); }
  void b() { t.g(); }
};

int main()
{
  Z<X> z1;
  z1.a(); // Z<X>::b() nie zostanie utworzony

  Z<Y> z2;
  z2.b(); // Z<X>::a() nie zostanie utworzony

  return 0;
}

Częściowa specjalizacja nie jest możliwa w przypadku szablonów funkcji. Rozwiązaniem może być przeciążenie szablonu.

Składowe statyczne

Szablon, który zawiera składowe statyczne, ma własne „wersje”, instancje danych. Jeżeli dla szablonu:


template <typename T> class A
{
private:
    static int g_nCount;
public:
    A()
    {
        g_nCount++;
    }

    static int GetCount() { return g_nCount; }
};

template <typename T> int A<T>::g_nCount = 0;

utworzymy następujące specjalizacje:


A<int> a1, a1bis;
A<float> a2;

if (a1.GetCount() != a2.GetCount()) // lub: if (A<int>::GetCount() != A<float>::GetCount())
{
  // obiekty mają różną liczbę instancji
}

Utworzone obiekty typu A<int>A<float>, mają różne wartości zmiennej g_nCount. Odpowiednio 2 i 1.

Zaprzyjaźnione funkcje i klasy

Tworzone szablony klas możemy zaprzyjaźniać z funkcjami i klasami, a także ich szablonami.


template <typename T>
class A
{
private:
  T m_n;

public:
  A(T n = 0) : m_n(n) {}
  T Get() { return m_n; }

  // zaprzyjaźniona funkcja dla wyspecjalizowanego szablonu klasy dla typu int
  friend void Function(A<int>&, int);

  // zaprzyjaźniony szablon funkcji
  template <typename U> friend void TFunction(A<U>& a, U v);

  // zaprzyjaźniona klasa (w implementacji używa specjalizacji szablonu klasy A dla typu int)
  friend class AIntFriend;

  // zaprzyjaźniony szablon klasy
  template <typename U> friend class TAFriend;
};

void Function(A<int>& a, int v) {  a.m_n = v; }

template <typename T> void TFunction(A<T>& a, T v) {  a.m_n = v; }

class AIntFriend
{
private:
  int m_n;
public:
  AIntFriend(int n = 0) : m_n(n)  {  }
  void Copy(A<int>& a)  {   a.m_n = m_n;  }

  int Get()  {   return m_n;  }
};

template <typename T> class TAFriend
{
private:
  T m_n;
public:
  TAFriend(T n = 0) : m_n(n)  {  }
  void Copy(A<T>& a)  {   a.m_n = m_n;  }
  T Get()  {   return m_n;  }
};

void usage()
{
  A<int> a1, a1bis;  A<float> a2;

  Function(a1, 3);  TFunction<int>(a1, 7);

  AIntFriend a1f(5);  a1f.Copy(a1);

  // atrybut m_n obiektów ma wartość '5'
  if (a1.Get() == a1f.Get())  { }

  TAFriend<float> a2f(1.3);  a2f.Copy(a2);

  // atrybut m_n obiektów ma wartość '1.3'
  if (a2.Get() == a2f.Get())  { }
}


0 Komentarzy

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *