Szablony funkcji

Artykuł został umieszczony na liście [[Zalazki artykulow|Zalążków artykułów]]. Jeżeli możesz rozbuduj go!

Szablony funkcji są sposobem na stworzenie funkcji, która może przyjmować argumenty dowolnych typów.

Przykładowo można stworzyć funkcje, która mnoży dwie liczby:

int pomnoz (int a, int b)
{
    return a * b;
}

Funkcja taka działa poprawnie dla argumentów typu int, gdy wyniknie potrzeba użycia jej dla typu float trzeba będzie ją całą skopiować i zapisać w postaci:

float pomnoz (float a, float b)
{
    return a * b;
}

Jak wiadomo w C++ może istnieć wiele funkcji o tej samej nazwie, różniących się tylko parametrami lub zwracanym typem. Więcej na ten temat w artykule Funkcje.

Potem skopiujemy ją jeszcze kilkakrotnie dla kolejnych typów które będą nam potrzebne, może będzie to unsigned int, char, double itp.

Rozwiązanie to jest mało eleganckie. Ponadto stwarza problemy - jeżeli w pierwszej funkcji wystąpi błąd trzeba będzie go poprawić we wszystkich jej kopiach, lepiej było by gdyby można było zrobić to tylko raz.

Z pomocą przychodzą szablony funkcji.

template <class Typ>
Typ pomnoz (Typ a, Typ b)
{
    return a * b;
}

Taki zapis oznacza, że nie tworzymy funkcji, tylko szablon. Z takiego szablonu będziemy tworzyć funkcje w obrębie programu zastępując słowo Typ pożądanym typem zmiennej.

Słowo kluczowe class przed nazwą parametru szablonu (w tym przypadku Typ) sugeruje, że szablony mogą, a nawet powinny, przyjmować jako argumenty typy zdefiniowane przez użytkownika (Klasy). Ten przykład jednak opiera się na typach wbudowanych dla ułatwienia zrozumienia idei szablonów.

#include <iostream>

using namespace std;

template <class Typ>
Typ pomnoz (Typ a, Typ b)
{
    return a * b;
}

int main()
{
    float fA = 0.3;
    float fB = 0.4;
    
    int iA = 2;
    int iB = 4;
    
    cout << "Wynik mnożenia liczb typu float: " << pomnoz<float>(fA,fB) << endl;
    cout << "Wynik mnożenia liczb typu int: " << pomnoz<int>(iA,iB) << endl;  
    
    return 0;   
}

Proszę zwrócić uwagę na wywołanie funkcji (a właściwie szablonu) pomnoz.

pomnoz<float>(fA,fB)

Oznacza to "zamień słówko Typ w moim szablonie na float" i skutkuje wytworzeniem takiej funkcji:

float pomnoz (float a, float b)
{
    return a * b;
}

Szablony mogą posiadać też więcej niż jeden argument:

#include <iostream>

using namespace std;

template <class TypPrzyjety, class TypZwracany>
TypZwracany rzutowanie (TypPrzyjety A)
{
    return (TypZwracany) A;
}

int main()
{
    float fA = 3.3;
    int iA = 0;
    
    iA = rzutowanie<float,int>(fA);
    
    cout << iA; // wyświetli 3
     
    return 0;   
}

Zobacz też

8 komentarzy

prgtw napisał(a)
  • mała liczba słów kluczowych

To niekoniecznie wada.

prgtw napisał(a)
  • brak kontroli zakresów np. tablic

Bo programista sam sprawdza tam, gdzie uważa to za stosowne - zysk na szybkości; zresztą w Pascalu też można wyłączyć sprawdzanie zakresów (Range Checking)

prgtw napisał(a)
  • brak zagnieżdżania funkcji w funkcji - nie w pełni strukturalny

Zagnieżdżanie funkcji w funkcji w Pascalu tylko spowalnia wykonywanie programu. Zresztą to nie jest jakieś dotkliwe ograniczenie.

prgtw napisał(a)
  • brak wbudowanego typu String, tylko tablice typu char, przez co operowanie na ciągach jest dla początkujących w C++ utrudnione

Ale w C++ masz klasę std::string ;>

prgtw napisał(a)
  • C++ nie mozna nazwać językiem wysokiego poziomu, ale niskiego też nie - średni poziom

IMHO jest to język wysokiego poziomu, ale te podziały są "umowne" - zależy jak na co patrzeć ;)

prgtw napisał(a)
  • niektóre kody źródłowe wydają mi się tak nieczytelne że to szok przez te znaki _ np. __cdecl, int __n itp.;

W Pascalu też można nieźle zaciemnić i nieładnie pisać kod [diabel] A te podwójne podkreślenia dolne to kwestia przyzwyczajenia, nie wygląda to aż tak źle ;)

prgtw napisał(a)

wskaźniki do wskaźników (zmiennych wskaźnikowych) np. char **x;

Też tak można w Pascalu ;)

marcinEc dobrze mówi - kto tak wywołuje funkcje:
rzutowanie<float,int>(fA);

to nie html...

a po co o tym pisać po raz kolejny...? Hmm, chyba tylko początkujący piszą artykuły, nie mając za sobą doświadczeń i całej tej (trudnej i nieraz całkiem szerokiej) wiedzy...
Ale może kiedyś... :)

marcinEc: to napisz o tym.. kiedy przyzwyczaicie sie do tego ze 4p uwolniło dokumenty...

A co z dedukcją parametrów? przecież nie muszę pisać:
foo<float>(zmf1,zmf2);

Jest jeszcze specjalizacja szablonu.

OT:
A dlaczego tak napisałeś. Czyżby Pascal był złym językiem? Narazie widzę więcej przeciw niż za:

  • mała liczba słów kluczowych
  • brak kontroli zakresów np. tablic
  • brak zagnieżdżania funkcji w funkcji - nie w pełni strukturalny
  • brak wbudowanego typu String, tylko tablice typu char, przez co operowanie na ciągach jest dla początkujących w C++ utrudnione
  • C++ nie mozna nazwać językiem wysokiego poziomu, ale niskiego też nie - średni poziom
  • niektóre kody źródłowe wydają mi się tak nieczytelne że to szok przez te znaki _ np. __cdecl, int __n itp.; wskaźniki do wskaźników (zmiennych wskaźnikowych) np. char **x;

Jest też trochę zalet, ale...
Jak możesz to napisz jakie są zalety z Twojego punktu widzenia (kogoś kto - tak myślę - orientuje się już dosyć dobrze).

hallelujah! another soul saved!

Powoli (choć nie wiem czemu, bo widzę więcej wad niż zalet) przerzucam się z Pascala na C++. Słyszałem o szablonach funkcji ale nie wiedziałem jak się ich używa, teraz już wiem. Dzięki.

OT: To tak jak w Pascalu funkcje z operatorem overload.