STL - Standard Template Library 2 (STL od źródeł, czyli specyfikacja biblioteki) Autor: Błażej Chodarcewicz rainbow.mimuw.edu.pl/~bc189380/STL/
Standard C++ (STL) zOrganizacja pracująca nad standardem zWersja Draft 1996 r. ISO-IEC Programming.Language.C++ (1998) z ISO/IEC 14882:2003
Jak to działa?
Iteratory zUogólnienie wstaźników zUmożliwiają pracę z kontenerami w ujednolicony sposób zBiblioteka formalizuje interfejs, semantykę oraz założenia złożonościowe zSemantyka iteratorów jest uogólnieniem semantyki wskaźników zWyrażenie *i – wartością jest obiekt pewnej klasy, enumeracja lub typ wbudowany T, zwany value type of iterator zWszystkie iteratory i, dla których (*i).m jest zdefiniowane, wspierają także wyrażenie i->m zDla każdego iteratora typu X, dla którego zdefiniowana jest równość, jest dostępny typ difference type iteratora
Rodzaje iteratorów zinput iterators zoutput iterators zforward iterators zbidirectional iterators zrandom access iterators
Relacje pomiędzy iteratorami InputOutput Forward Bidirectional Random Access
Iteratory zmienialne i niezmienialne zmutable, constant iterators z*i jako referencja lub referencja do stałej zIteratory stałe nie spełniają wymagań output iterators
past-the-end value zDla każdego iteratora istnieje wartość wskazująca na element za ostatnim elementem kolekcji, z którą związany jest iterator zdereferencable values zBiblioteka nigdy nie zakłada, że past-the-end value jest dereferencable
Kilka definicji zIterator i jest reachable z iteratora i wtw., gdy istnieje skończona sekwencja aplikacji wyrażenia ++i, po której mamy i == j. zJeśłi i jest reachable z j, to i i j odnoszą się do tej samej kolekcji zRange – para iteratorów, które określają początek i koniec obliczeń zRange [i, i) – empty range zW ogólności range [i, j) odnosi się do elementów struktury danych zaczynających się od elementu wskazywanego przez i, kończących się na elemencie wskazywanym przez j bez tego elementu zRange [i, j) jest prawidłowe wtw., gdy j jest reachable z i. Aplikacja algorytmu dla nieprawidłowego Range jest nieokreślona
Kilka założeń zZłożoność (zamortyzowana) wszystkich operacji wymaganych dla danej kategorii iteratora jest stała zNa następnych slajdach występują: a, b oznaczają wartości iteratora X, n oznacza wartość typu difference type Distance; u, tmp i m oznaczają identyfikatory, r oznacza wartość X&, t oznacza wartość typu wartości iteratora - T.
Input iterator - wymagania OperacjaTypSemantyka, założenia X u(a); X post: u jest kopią a Destruktor musi być zdefiniowany i dostępny. u = a; X wynik: u post: u jest kopią a a == bconvertible to bool == jest relacją równości (w matematycznym sensie) zdefiniowaną na dziedzinie wartości iteratora a != bconvertible to bool bool(a==b) != bool(a!=b) dla dziedziny == *a T pre: a jest dereferenceable Jeśli a==b i (a,b) należy do dziedziny relacji ==, wtedy *a jest takie samo jak *b. a->mpre: (*a).m jest dobrze zdefiniowane Semantyka jest taka sama jak (*a).m
Input iterator – wymagania c.d. OperacjaTypSemantyka, założenia ++r X& pre: r jest dereferenceable post: r jest dereferenceable lub r jest wartością past-the- end post: każda kopia poprzednij wartości r nie musi już być ani dereferenceable, ani nawet nie musi być w dziedzinie == (void)r++ taka sama jak (void)++r *r++ T{ T tmp = *r; ++r; return tmp; }
Input iterator – uwagi za == b nie implikuje ++a == ++b zAlgorytmy nigdy nie powinny próbować przechodzi po tym samym iteratorze dwa razy zTyp wartości T nie musi być typem lvalue (Przykład istream_iterator)
Output iterator - wymagania OperacjaZwracany typ Semantyka, założenia X(a) X a = t jest takie samo jak X(a) = t Zakłada się obecność destruktora X u(a); SGI: pre: a musi być zainicjalizowane post: *a = t jest takie samo jak *u = t X u = a; *a = t nieużywanySGI: pre: a jest dereferencable po poprzednim przypisaniu było wykonane zwiększenie a ++rX&&r == &++r r++ konwertowalny do const X& { X tmp = r; const X& ++r; return tmp; } *r++ = t nieużywany
Output iterator - uwagi zoperator * może zostać użyty jedynie po lewej stronie wyrażenia przypisania zprzypisanie poprzez wartość iteratora odbywa się tylko raz znigdy nie należy przechodzić po tych samych wartościach iteratora więcej niż raz z== i != mogą być niezdefiniowane
Forward iterator - wymagania OperacjaTypSemantyka, założenia X u; u może być niezainicjowane, zakładane jest istnienie destruktora X u(a); X u = a; Xrównoważne: X u; u = a; post: u == a X(a) X X(a) == a a == bconvertible to bool == jest relacją równości (w matematycznym sensie) zdefiniowaną na dziedzinie wartości iteratora a != bconvertible to bool !(a==b) r = a X& post: r == a
Forward iterator – wymagania c.d. OperacjaTypSemantyka, założenia *a T& pre: a jest dereferenceable Jeśli a == b, to *a == *b Jeśli X jest typem mutowalnym, to *a = t jest poprawne a->mpre: (*a).m jest dobrze zdefiniowane Semantyka jest taka sama jak (*a).m ++r X& pre: r jest dereferenceable post: r jest dereferenceable lub r jest wartością past- the-end Jeśli r == s i r jest dereferencable, to ++r == ++s. &r == &++r r++ konwertowalny do const X& { X tmp = r; ++r; return tmp; } *r++ T&
Bidirectional iterator – dodatkowe wymagania OperacjaTypSemantyka, założenia --r X& pre: istnieje s takie, że r == ++s. post: r jest dereferenceable. Jeśli –r == --s, to r == s. &r == &--r. --(++r) = r. r-- konwertowalny do const X& { X tmp = r; --r; return tmp; } *r-- konwertowalny do T
Random access iterator - wymagania OperacjaTypSemantyka, założenia r += n X& { Distance m = n; if (m >= 0) while (m--) ++r; else while (m++) --r; return r; } a + n n + a X { X tmp = a; return tmp += n; } założenie: a + n == n + a r -= nX& return r += -n; a – nX{ X tmp = a; return tmp -= n; } b – a Distancepre: Istnieje wartość n typu Distance taka, że a + n == b. b == a + (b – a). (a < b) ? distance(a, b) : -distance(b, a).
Random access iterator – wymagania c.d. OperacjaTypSemantyka, założenia a[n] konwertowalny do T *(a + n) a < b konwertowalny do bool true wtw., gdy b – a > 0. > jest relacją porządku liniowego. a > bkonwertowaln y do bool równoważne z b < a. > jest relacją porządku liniowego a <= bkonwertowaln y do bool takie samo jak: !(a > b) a >= bkonwertowaln y do bool takie samo jak: !(a < b)
Przykłady int main(){ int array [1000], *i; int n = 0; i = array; while (cin >> *i) ++i; ++i; sort (array, i); for (j = array; i != j; j++) cout << *j << "\n"; }
Przykład int main () { vector v; int input; while (cin >> input) v.push_back (input); sort(v.begin(), v.end()); for (vector ::iterator i = v.begin(); i != v.end(); i++) cout << *i << "\n"; }
Obiekty funkcyjne zObiekty funkcyjne to obiekty, które mają zdefiniowany operator () zSą ważną częścią biblioteki STL, zapewniają one efektywność zWszędzie tam, gdzie szablony algorytmów oczekują wskaźników do funkcji, można stosować obiekty funkcyjne zUżywanie obiektów funkcyjnych razem z szablonami funkcji zwiększa siłę wyrazu biblioteki, a także zwiększa efektywność kodu
Obiekty funkcyjne – przykłady zdodanie elementów dwóch wektorów (double) a, b do siebie transform(a.begin(), a.end(), b.begin(), a.begin(), plus ()); zzanegowanie wszystkich elementów a: transform(a.begin(), a.end(), a.begin(), negate ());
Obiekty funkcyjne template struct unary_function { typedef Arg argument_type; typedef Result result_type; }; template struct binary_function { typedef Arg1 first_argument_type; typedef Arg2 second_argument_type; typedef Result result_type; };
Obiekty funkcyjne – przykłady ztemplate struct plus : binary_function { T operator()(const T& x, const T& y) const; }; Obiekt funkcyjny obliczający x + y. ztemplate struct negate : unary_function { T operator()(const T& x) const; }; Obiekt funkcyjny obliczający –x.
Obiekty funkcyjne – przykłady ztemplate class binder1st : public unary_function { protected: Operation op; Operation::first_argument_type value; public: binder1st(const Operation& x, const Operation::first_argument_type& y); result_type operator()(const argument_type& x) const; }; zKonstruktor inicjalizuje op na x, value na y zOperator () zwraca op(value, x) ztemplate binder1st bind1st(const Operation& op, const T& x);
EqualityComparable Typ T jest EqualityComparable jeśli ma zdefiniowany operator ==, którego wynik jest konwertowalny do bool oraz z== jest relacją równości spełniającą założenia: - Dla każdego a: a == a - Jeśli a == b, to b == a - Jeśli a == b i b == c, to a == c
Algorytmy Non-mutating ztemplate UnaryFunction for_each(InputIterator first, InputIterator last, UnaryFunction f); Efekt: Zaaplikowanie f do wyników dereferencii kolejnych wartości iteratora w przedziale [first, last), zaczynając od first i kontynuując aż do last - 1. Wymagania: f nie powinno wywoływać żadnych funkcji, które nie są zadeklarowane jako stałe dla wartości iteratora. Zwraca: f. Złożoność: f jest wywoływana dokładnie last - first razy. Uwaga: Jeśli f zwraca jakąś wartość wynik jest ignorwany. ztemplate iterator_traits ::difference_type count(InputIterator first, InputIterator last, const EqualityComparable& value); Wymagania: Typ T jest EqualityComparable. Efekt: Zwraca liczbę iteratorów i z przedziału [first, last), dla których warunek: *i == value.
Algorytmy Non-mutating ztemplate InputIterator find(InputIterator first, InputIterator last, const EqualityComparable& value) Wymagania: Typ T is EqualityComparable. Wynik: Pierwszy iterator i z przedziału [first, last), dla którego prawdziwy jest warunek: *i == value. Jeśli żaden iterator z tego przedziału nie spełnia tego warunku zwracany jest last. ztemplate OutputIterator copy(InputIterator first, InputIterator last, OutputIterator result); Efekt: kopiuje elementy z przedziału [first, last) do [result, result+(last- first)) zaczynając od first i kontynuując aż do last. Dla każdej nieujemnej liczby n < (last-first), wykonuje *(result+n) = *(first+n). Wynik: result + (last - first). Wymagania: result nie powinien być z przedziału [first, last). Złożoność: dokładnie last - first przypisań.
Przykład: template struct print : public unary_function { print(ostream& out) : os(out), count(0) {} void operator() (T x) { os << x << ' '; ++count; } ostream& os; int count; }; int main(){ int A[] = {1, 4, 2, 8, 5, 7}; const int N = sizeof(A) / sizeof(int); print P = for_each(A, A + N, print (cout)); cout << endl << P.count << " objects printed." << endl; }
Gdzie szukać informacji? zMusser Saini, STL Tutorial and Reference z Lippman, "Istota jezyka C++ zN.M.Josuttis, C++ bibliotek standardowa podrecznik programisty zwww-d0.fnal.gov/~dladams/cxx_standard.pdf zhttp://anubis.dkuug.dk/jtc1/sc22/open/n2356/
Gdzie szukać informacji? zhttp:// - implementacja STL firmy Silicon Graphics, Inc. (SGI) zhttp:// - książka "Designing Components with the C++ STL" zhttp:// - strona o STL z 1995 rokuhttp:// zhttp:// - prosty tutorialhttp:// zhttp:// - krótki opis STL'ahttp:// zhttp://pages.cpsc.ucalgary.ca/~kremer/STL/1024x768/index.html - strona o STL'uhttp://pages.cpsc.ucalgary.ca/~kremer/STL/1024x768/index.html
Dziękuję za uwagę!