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