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