Od kilku miesięcy znamy nowy standard języka C++, noszący nieoficjalną nazwę C++11. Najpopularniejsze kompilatory stopniowo dostosowują się do nowego standardu. Warto wiedzieć co nowego zostało dodane do języka, gdyż niektóre rzeczy na pewno ułatwią i przyspieszą pisanie kodu. Będę starał się w miarę regularnie przybliżać funkcjonalności, które działają już w najnowszych wersjach wielu kompilatorów. Na początek: pętla for oparta na zakresie.
1. Składnia i zastosowanie
Działanie nowej pętli najłatwiej będzie wytłumaczyć na przykładzie:
vector<int> vec; vec.push_back(1); vec.push_back(2); vec.push_back(3); for(int i : vec) { cout << i; }
Powyższy kod wypisuje na standardowe wyjście każdy element wektora vec. Ważne jest, aby możliwa była konwersja elementów wektora to typu zmiennej i. Nie jest możliwe zatem użycie następującego kodu:
vector<int> vec; vec.push_back(1); vec.push_back(2); vec.push_back(3); for(string i : vec) { cout << i; // błąd kompilacji! }
We wcześniejszym przykładzie zmienna i za każdym razem przechowywała kopię danego elementu wektora vec. Edycja zmiennej i wewnątrz pętli, nie zmieniłaby zatem elementów wektora. Nic nie stoi jednak na przeszkodzie, by zmienna i była na przykład referencją do elementów wektora vec:
vector<int> vec; vec.push_back(1); vec.push_back(2); vec.push_back(3); for(int& i : vec) { ++i; }
Po wykonaniu powyższego kodu wektor vec przechowywać będzie wartości 2, 3 i 4.
Pętla for oparta na zakresie działa dla wszystkich kontenerów wchodzących w skład biblioteki STL, typów string oraz nawet zwykłych tablic:
int array[] = {11, 22, 33, 44, 55}; for(int& x : array) { x *= 2; }
2. Tworzenie własnych typów kompatybilnych z nową pętlą
Nowa pętla może być użyta z każdym typem, po którym jest możliwa iteracja, a więc musi on "definiować jakiś zakres". Jeżeli chcielibyśmy zastosować pętlę dla stworzonego przez nas typu, musi on być zgodny z konwencją przyjętą w bibliotece STL i zdefiniowanymi tam iteratorami:
- Muszą istnieć metody begin oraz end, zdefiniowane jako składowe klasy bądź wolne funkcje, zwracające iteratory odpowiednio na początek i koniec sekwencji.
- Iteratory muszą działać z operatorami *, != oraz prefiksowym ++.
Poniżej znajduje się przykładowy program demonstrujący użycie nowej pętli for z typem zdefiniowanym przez użytkownika.
#include <algorithm> #include <cstddef> #include <cstdio> template<typename T, std::size_t N> class MyArray; template<typename T, std::size_t N> class MyArrayIter { public: MyArrayIter(const MyArray<T, N>& myArray_, std::size_t pos_) : myArray(myArray_), pos(pos_) {} bool operator!=(const MyArrayIter<T, N>& iter) { return pos != iter.pos; } T operator*() const; const MyArrayIter<T, N>& operator++() { ++pos; return *this; } private: const MyArray<T, N>& myArray; std::size_t pos; }; template<typename T, std::size_t N = 3> class MyArray { public: typedef MyArrayIter<T, N> iterator; MyArray() { std::fill(data, data + N, T()); // wypełnianie wartościami domyślnymi } iterator begin() const { return MyArrayIter<T, N>(*this, 0); } iterator end() const { return MyArrayIter<T, N>(*this, N); } T operator[](std::size_t i) const { return data[i]; } private: T data[N]; }; template<typename T, std::size_t N> T MyArrayIter<T, N>::operator*() const { return myArray[pos]; } int main() { MyArray<int> myArray; for(int x : myArray) { // iteracja po typie użytkownika printf("%d\n", x); } }
Powyższy program wyświetla trzy zera. ;)
3. Wsparcie kompilatorów
Pętla for oparta na zakresie działa w kompilatorach Clang od wersji 3.0 oraz GCC od wersji 4.6. Microsoft Visual C++ będzie posiadał nową pętlę w wersji 2011 (aktualnie dostępna w VC11 Beta).
Pliki do pobrania:
Brak komentarzy:
Prześlij komentarz