Last modified: October 10, 2024

This article is written in: 馃嚨馃嚤

Szablony

Szablony (ang. templates) stanowi膮 fundament nowoczesnego programowania w j臋zyku C++. Umo偶liwiaj膮 one tworzenie kodu generycznego, kt贸ry mo偶e dzia艂a膰 z r贸偶nymi typami danych bez konieczno艣ci jego duplikacji. Szablony s膮 kluczowym elementem metaprogramowania w C++, pozwalaj膮c na wykonywanie oblicze艅 na etapie kompilacji i optymalizacj臋 kodu wynikowego.

Wprowadzenie do Szablon贸w

Szablony w C++ s膮 mechanizmem pozwalaj膮cym na tworzenie funkcji, klas, a nawet zmiennych, kt贸re s膮 parametryzowane typami lub warto艣ciami sta艂ymi. Dzi臋ki temu mo偶emy pisa膰 kod, kt贸ry jest niezale偶ny od konkretnego typu danych, co zwi臋ksza jego reu偶ywalno艣膰 i elastyczno艣膰.

Szablony Funkcji

Szablony funkcji pozwalaj膮 na definiowanie funkcji, kt贸re dzia艂aj膮 na r贸偶nych typach danych. Og贸lna sk艂adnia definicji szablonu funkcji jest nast臋puj膮ca:

template <parametry_szablonu>
typ_zwracany nazwa_funkcji(argumenty) {
    // cia艂o funkcji
}

Przyk艂ad: Definicja generycznej funkcji max2, kt贸ra zwraca wi臋ksz膮 z dw贸ch warto艣ci:

template <typename t="">
T max2(T arg1, T arg2) {
    return arg1 > arg2 ? arg1 : arg2;
}

Wyja艣nienie:

U偶ycie funkcji szablonowej:

int a = max2<int>(10, 20);            // Wynik: 20
double b = max2<double>(16.2, 3.14);  // Wynik: 16.2
char c = max2<char>('a', 'b');        // Wynik: 'b'

Mechanizm Instancjacji

Podczas kompilacji, gdy funkcja szablonowa jest wywo艂ywana z konkretnym typem, kompilator tworzy jej instancj臋 dla tego typu. Proces ten nazywany jest instancjacj膮 szablonu. Dzi臋ki temu generowany kod jest zoptymalizowany pod k膮tem u偶ytych typ贸w, eliminuj膮c narzut wydajno艣ciowy zwi膮zany z polimorfizmem dynamicznym.

Szablony Klas

Szablony klas umo偶liwiaj膮 definiowanie klas generycznych, kt贸re mog膮 operowa膰 na r贸偶nych typach danych. Sk艂adnia szablonu klasy jest podobna do szablonu funkcji.

Przyk艂ad: Definicja szablonu klasy Box:

template <typename t="">
class Box {
private:
    T content;

public:
    Box(T content) : content(content) {}
    T getContent() const { return content; }
};

Wyja艣nienie:

Tworzenie instancji szablonu klasy:

Box<int> intBox(42);
Box<std::string> stringBox("Witaj");

Wielokrotne Parametry Szablonu

Szablony mog膮 przyjmowa膰 wiele parametr贸w, zar贸wno typ贸w, jak i warto艣ci sta艂ych. Pozwala to na bardziej precyzyjne parametryzowanie kodu.

Przyk艂ad: Szablon klasy Array z dwoma parametrami:

template <typename size="" std::size_t="" t,="">
class Array {
private:
    T elements[Size];

public:
    // Metody dost臋pu i modyfikacji element贸w
};

U偶ycie:

Array<int, 5=""> myArray;

Specjalizacja Szablonu

Czasami istnieje potrzeba dostosowania zachowania szablonu dla konkretnego typu. W takich przypadkach u偶ywamy specjalizacji szablonu.

Przyk艂ad: Specjalizacja szablonu klasy Box dla typu std::string:

template <>
class Box<std::string> {
private:
    std::string content;

public:
    Box(std::string content) : content(content) {}
    std::string getContent() const { return "Zawarto艣膰: " + content; }
};

Wyja艣nienie:

Typy Domy艣lne w Szablonach

Mo偶emy definiowa膰 warto艣ci domy艣lne dla parametr贸w szablonu, co zwi臋ksza elastyczno艣膰 ich u偶ycia.

Przyk艂ad:

template <typename size="10" std::size_t="" t="int,">
class Array {
private:
    T elements[Size];

public:
    // Implementacja metod
};

U偶ycie:

Array<> defaultArray;             // Typ T=int, Size=10
Array<double, 5=""> customArray;     // Typ T=double, Size=5

Szablony Zmiennych

Od C++14 mo偶liwe jest definiowanie szablon贸w zmiennych, co pozwala na tworzenie zmiennych parametryzowanych typem.

Przyk艂ad:

template<typename t="">
constexpr T pi = T(3.1415926535897932385);

auto floatPi = pi<float>;
auto doublePi = pi<double>;

Wyja艣nienie:

Aliasowanie Szablon贸w

C++11 wprowadzi艂 mo偶liwo艣膰 tworzenia alias贸w szablon贸w za pomoc膮 s艂owa kluczowego using. U艂atwia to prac臋 z z艂o偶onymi typami szablonowymi.

Przyk艂ad:

template <typename t="">
using Vec = std::vector<t>;

Vec<int> intVector;
Vec<double> doubleVector;

Szablony Lambda

Od C++20 mo偶na tworzy膰 szablony funkcji lambda, co dodatkowo zwi臋ksza mo偶liwo艣ci programistyczne.

Przyk艂ad:

auto lambda = []<typename t="">(T a, T b) {
    return a + b;
};

auto sumInt = lambda(5, 3);           // Wynik: 8
auto sumDouble = lambda(2.5, 1.5);    // Wynik: 4.0

Metaprogramowanie Szablonowe

Metaprogramowanie szablonowe to technika, kt贸ra wykorzystuje szablony do wykonywania oblicze艅 na etapie kompilacji. Pozwala to na optymalizacj臋 kodu i wykonywanie skomplikowanych oblicze艅 bez narzutu w czasie wykonywania programu.

Przyk艂ad: Obliczanie Liczby Fibonacciego

Obliczanie warto艣ci ci膮gu Fibonacciego za pomoc膮 szablon贸w:

template<int n="">
struct Fibonacci {
    static_assert(N >= 0, "N musi by膰 nieujemne");
    static constexpr int value = Fibonacci<n -="" 1="">::value + Fibonacci<n -="" 2="">::value;
};

template<>
struct Fibonacci<0> {
    static constexpr int value = 0;
};

template<>
struct Fibonacci<1> {
    static constexpr int value = 1;
};

constexpr int fib10 = Fibonacci<10>::value; // Wynik: 55

Wyja艣nienie:

Analiza Matematyczna

Ci膮g Fibonacciego jest zdefiniowany rekurencyjnie:

$$ F(0) = 0, \quad F(1) = 1, \quad F(N) = F(N-1) + F(N-2) \text{ dla } N \geq 2 $$

Implementacja za pomoc膮 szablon贸w odwzorowuje t臋 definicj臋, pozwalaj膮c kompilatorowi na obliczenie warto艣ci F(N) podczas kompilacji.

Zastosowania Metaprogramowania Szablonowego

Zastosowanie Szablon贸w w Praktyce

Szablony s膮 integraln膮 cz臋艣ci膮 j臋zyka C++ i stanowi膮 podstaw臋 wielu bibliotek oraz aplikacji komercyjnych. Ich zdolno艣膰 do tworzenia kodu generycznego, kt贸ry mo偶e dzia艂a膰 z r贸偶nymi typami danych, sprawia, 偶e s膮 one niezb臋dne w nowoczesnym programowaniu. W tej sekcji przyjrzymy si臋, jak szablony s膮 wykorzystywane w praktyce, skupiaj膮c si臋 na standardowej bibliotece C++ (STL) oraz na innych popularnych bibliotekach, takich jak Boost czy Eigen.

Standardowa Biblioteka Szablon贸w (STL)

STL (Standard Template Library) jest zestawem klas i funkcji szablonowych dostarczanych przez standardow膮 bibliotek臋 C++. Zawiera ona kontenery, algorytmy oraz iteratory, kt贸re umo偶liwiaj膮 efektywne i elastyczne manipulowanie danymi.

Kontenery

Kontenery s膮 klasami szablonowymi, kt贸re przechowuj膮 kolekcje obiekt贸w. Dzi臋ki szablonom mog膮 one przechowywa膰 elementy dowolnego typu. Oto niekt贸re z najwa偶niejszych kontener贸w w STL:

Kontener Opis
std::vector Dynamiczna tablica o zmiennym rozmiarze.
std::list Lista dwukierunkowa.
std::deque Dwustronna kolejka.
std::set Zbi贸r unikalnych element贸w, uporz膮dkowanych.
std::map Asocjacyjny kontener przechowuj膮cy pary klucz-warto艣膰.
std::unordered_set Nieuporz膮dkowany zbi贸r wykorzystuj膮cy tablice haszuj膮ce.
std::unordered_map Nieuporz膮dkowana mapa wykorzystuj膮ca tablice haszuj膮ce do par klucz-warto艣膰.

Algorytmy

Algorytmy w STL s膮 funkcjami szablonowymi, kt贸re wykonuj膮 operacje na danych przechowywanych w kontenerach. S膮 one niezale偶ne od konkretnych typ贸w danych i kontener贸w, o ile dostarczone s膮 odpowiednie iteratory.

Funkcja Opis
std::sort Sortowanie element贸w w zakresie.
std::find Wyszukiwanie elementu w zakresie.
std::accumulate Sumowanie warto艣ci w zakresie.
std::copy Kopiowanie element贸w z jednego zakresu do drugiego.

Iteratory

Iteratory s膮 abstrakcj膮 wska藕nik贸w, kt贸re pozwalaj膮 na jednolite interfejsy do przegl膮dania element贸w w kontenerach. S膮 one zaimplementowane jako szablony, dzi臋ki czemu mog膮 dzia艂a膰 z r贸偶nymi typami kontener贸w.

Przyk艂ad: Kontener std::vector

std::vector jest jednym z najcz臋艣ciej u偶ywanych kontener贸w w STL. Reprezentuje dynamiczn膮 tablic臋, kt贸ra mo偶e zmienia膰 sw贸j rozmiar w czasie wykonywania programu. Dzi臋ki wykorzystaniu szablon贸w, std::vector mo偶e przechowywa膰 elementy dowolnego typu.

Definicja szablonu std::vector:

W uproszczeniu, std::vector jest zdefiniowany nast臋puj膮co:

template <typename allocator="std::allocator<T" t,="" typename="">>
class vector {
    // Implementacja wewn臋trzna
};

Parametry szablonu:

Przyk艂ady u偶ycia:

std::vector<int> vecInt;            // Wektor liczb ca艂kowitych
std::vector<double> vecDouble;      // Wektor liczb zmiennoprzecinkowych
std::vector<std::string> vecString; // Wektor 艂a艅cuch贸w znak贸w

Biblioteka Boost

Boost to zestaw bibliotek C++ rozszerzaj膮cych funkcjonalno艣膰 standardowej biblioteki. Wiele z nich jest proponowanych do w艂膮czenia do standardu C++. Szablony s膮 intensywnie wykorzystywane w celu zapewnienia elastyczno艣ci i wydajno艣ci.

Przyk艂ad: boost::shared_ptr

Przed wprowadzeniem std::shared_ptr w C++11, boost::shared_ptr by艂 szeroko stosowanym inteligentnym wska藕nikiem zarz膮dzaj膮cym 偶yciem obiektu.

Definicja:

template<typename t="">
class shared_ptr {
    // Implementacja wewn臋trzna
};

U偶ycie:

boost::shared_ptr<myclass> ptr(new MyClass());

Zalety:

Biblioteka Eigen

Eigen to szablonowa biblioteka C++ do algebry liniowej, zoptymalizowana pod k膮tem wysokiej wydajno艣ci.

Definicja szablonu macierzy:

template<typename colsatcompiletime="" int="" rowsatcompiletime,="" scalar,="">
class Matrix {
    // Implementacja wewn臋trzna
};

Przyk艂ad u偶ycia:

Eigen::Matrix<float, 3="" 3,=""> matA;
Eigen::Matrix<float, 1="" 3,=""> vecB;

matA << 1, 2, 3,
         4, 5, 6,
         7, 8, 9;

vecB << 1,
         2,
         3;

Eigen::Matrix<float, 1="" 3,=""> result = matA * vecB;

W艂a艣ciwo艣ci:

Analiza wydajno艣ci:

Koncepty (C++20)

Koncepty wprowadzaj膮 mo偶liwo艣膰 definiowania wymaga艅 dla parametr贸w szablonu, co u艂atwia tworzenie bardziej czytelnego i bezpiecznego kodu.

Przyk艂ad:

template<typename t="">
concept Number = std::is_arithmetic_v<t>;

template<number t="">
T multiply(T a, T b) {
    return a * b;
}

Zalety:

Zaawansowane Techniki z Szablonami

Szablony o Zmiennej Liczbie Argument贸w

C++11 wprowadzi艂 szablony o zmiennej liczbie argument贸w (ang. variadic templates), kt贸re pozwalaj膮 na definiowanie funkcji i klas przyjmuj膮cych dowoln膮 liczb臋 parametr贸w.

Przyk艂ad: Funkcja print wy艣wietlaj膮ca dowoln膮 liczb臋 argument贸w:

template<typename... args="">
void print(Args... args) {
    (std::cout << ... << args) << std::endl;
}

print(1, 2, 3);                 // Wy艣wietla: 123
print("Witaj, ", "艣wiecie!");   // Wy艣wietla: Witaj, 艣wiecie!

Wyja艣nienie:

Wyra偶enia constexpr w Szablonach

constexpr pozwala na wykonywanie oblicze艅 w czasie kompilacji. Gdy u偶ywamy go w szablonach, mo偶emy tworzy膰 funkcje, kt贸re zwracaj膮 sta艂e warto艣ci zale偶ne od parametr贸w szablonu.

Przyk艂ad: Funkcja square obliczaj膮ca kwadrat liczby:

template<typename t="">
constexpr T square(T x) {
    return x * x;
}

constexpr int squareOfFive = square(5); // Wynik: 25

Szablony Wewn臋trzne (Curiously Recurring Template Pattern - CRTP)

CRTP to idiom programistyczny, w kt贸rym klasa dziedziczy po szablonie swojej w艂asnej klasy.

Przyk艂ad:

template<typename derived="">
class Base {
public:
    void interface() {
        static_cast<derived*>(this)->implementation();
    }
};

class DerivedClass : public Base<derivedclass> {
public:
    void implementation() {
        // Implementacja specyficzna dla klasy pochodnej
    }
};

Zastosowania:

Ograniczenia i Wyzwania

Praktyczne Wskaz贸wki

Spis Tre艣ci

  1. Wprowadzenie do Szablon贸w
    1. Szablony Funkcji
    2. Mechanizm Instancjacji
  2. Szablony Klas
  3. Wielokrotne Parametry Szablonu
  4. Specjalizacja Szablonu
  5. Typy Domy艣lne w Szablonach
  6. Szablony Zmiennych
  7. Aliasowanie Szablon贸w
  8. Szablony Lambda
  9. Metaprogramowanie Szablonowe
    1. Przyk艂ad: Obliczanie Liczby Fibonacciego
    2. Analiza Matematyczna
    3. Zastosowania Metaprogramowania Szablonowego
  10. Zastosowanie Szablon贸w w Praktyce
    1. Standardowa Biblioteka Szablon贸w (STL)
      1. Kontenery
      2. Algorytmy
      3. Iteratory
    2. Przyk艂ad: Kontener std::vector
    3. Biblioteka Boost
    4. Biblioteka Eigen
  11. Koncepty (C++20)
  12. Zaawansowane Techniki z Szablonami
    1. Szablony o Zmiennej Liczbie Argument贸w
    2. Wyra偶enia constexpr w Szablonach
      1. Szablony Wewn臋trzne (Curiously Recurring Template Pattern - CRTP)
    3. Ograniczenia i Wyzwania
    4. Praktyczne Wskaz贸wki