2009-10-26 11 views
5

Minęło trochę czasu od Visual Studio dodano wsparcie dla rozszerzenia foreach, który działa jakJak napisać klasę zdolnego foreach

vector<int> v(3) 
for each (int i in v) { 
    printf("%d\n",i); 
} 

Chcę wiedzieć, jak sprawić, by każda klasa w stanie używać foreach. Czy muszę wdrożyć jakiś interfejs?

+0

MS kompilator nigdy obsługiwany anyting tak dla kodu C++. Sądzę, że mówisz o jakimś innym języku. DO#? Jeśli tak, musisz powtórzyć swoje pytanie. – AnT

+3

http://msdn.microsoft.com/en-us/library/ms177202%28VS.80%29.aspx – stelonix

+2

Być może tag C++ powinien być tagiem vC++. Z pewnością nie jest to standardowe pytanie w języku C++. – MAK

Odpowiedz

10

for each oświadczenie w VC++, kiedy jest stosowany na nie zarządzane Klasa:

for each (T x in xs) 
{ 
    ... 
} 

jest po prostu cukier syntaktyczny na to:

for (auto iter = xs.begin(), end = xs.end(); iter != end; ++iter) 
{ 
    T x = *iter; 
} 

Gdzie auto oznacza, że ​​typ zmiennej jest wyprowadzona automatycznie typ inicjalizatora.

Innymi słowy, musisz podać metody klasy begin() i end(), które zwróciłyby dla niego początkowe i końcowe iteratory wejściowe.

Oto przykład z klasy, która owija się istream i umożliwia iteracyjne nad wszystkie linie w nim:

#include <istream> 
#include <iostream> 
#include <fstream> 
#include <string> 


class lines 
{ 
public: 

    class line_iterator 
    { 
    public: 

     line_iterator() : in(0) 
     { 
     } 

     line_iterator(std::istream& in) : in(&in) 
     { 
      ++*this; 
     } 

     line_iterator& operator++() 
     { 
      getline(*in, line); 
      return *this; 
     } 

     line_iterator operator++ (int) 
     { 
      line_iterator result = *this; 
      ++*this; 
      return result; 
     } 

     const std::string& operator*() const 
     { 
      return line; 
     } 

     const std::string& operator->() const 
     { 
      return line; 
     } 

     friend bool operator== (const line_iterator& lhs, const line_iterator& rhs) 
     { 
      return (lhs.in == rhs.in) || 
        (lhs.in == 0 && rhs.in->eof()) || 
        (rhs.in == 0 && lhs.in->eof()); 
     } 

     friend bool operator!= (const line_iterator& lhs, const line_iterator& rhs) 
     { 
      return !(lhs == rhs); 
     } 

    private: 

     std::istream* const in; 
     std::string line; 
    }; 


    lines(std::istream& in) : in(in) 
    { 
    } 

    line_iterator begin() const 
    { 
     return line_iterator(in); 
    } 

    line_iterator end() const 
    { 
     return line_iterator(); 
    } 

private: 

    std::istream& in; 
}; 


int main() 
{ 
    std::ifstream f(__FILE__); 
    for each (std::string line in lines(f)) 
    { 
     std::cout << line << std::endl; 
    } 
} 

Należy zauważyć, że realizacja line_iterator jest faktycznie nieco większy niż minimum potrzebnego for each; Jednakże, jest to minimalna realizacja zgodnych z wymaganiami iterator wejściowy, a więc klasa ta jest również użyteczny ze wszystkich algorytmów STL, które działają na iteratorów wejściowych, takich jak std::for_each, std::find itp

+0

możesz podać najprostszy przykład klasy implementującej kompilowane? Wystarczy znać metody zwracania metod i cokolwiek innego brakuje, ponieważ nie jestem w stanie skompilować się z prostą klasą – stelonix

+0

Musisz podać iteratory początku i końca. – GManNickG

+0

Zaktualizowana odpowiedź na przykład. Rzeczywiste typy powrotów nie mają znaczenia, o ile obsługują wszystkie operacje, które są implikowane przez opisane rozszerzenie 'dla każdego'. –

0

Foreach nie jest częścią języka C++, o ile wiem. Jest jednak w języku C#. Ponadto, myślę, że STL i/lub Boost ma metodę foreach. Być może myślisz o tym?

+1

Jestem świadomy, że to nie jest część tego języka i jestem również świadomy, że istnieje foreach w STL i w Boost. Jednak kompilator Visual Studio obsługuje foreach w sposób pokazany jako rozszerzenie kompilatora i chciałbym wiedzieć, jak tworzyć klasy, które z nim współpracują. – stelonix

+0

Ah w porządku, przepraszam za nieporozumienie. –

+0

Rewizja za nieporozumienie, a następnie przepraszanie za to? Okazało się, że ktoś szedł na dół. –

1

Można użyć tej

std::for_each

+0

+1 Jeśli to możliwe, preferuj standardy do niestandardowych rozszerzeń. –

+3

'std :: for_each' nie jest tak naprawdę zamiennikiem, ponieważ nie pozwala określić bloku kodu do wykonania dla każdej iteracji inline. W praktyce prawie nigdy nie widzę używanego 'std :: for_each' właśnie z tego powodu - po prostu łatwiej jest napisać dobrą starą pętlę' for' z iteratorami. Jeśli chodzi o "dla każdego" - na pewno nie ma żadnego miejsca w przenośnym kodzie, ale i tak nie cały kod jest sensownie przenośny. Jeśli twoja aplikacja jest już napisana przy użyciu MFC lub ATL, to i tak utknąłeś z VC++, i nie ma nic złego w używaniu jego rozszerzeń językowych. –

+0

Jeśli przenośność nie jest wymagana, chciałbym dodać funkcje lambda do puli i głosować tą odpowiedzią w górę. Z pewnością będą one implementowane przez niektóre kompilatory już teraz lub wkrótce i rzeczywiście sprawią, że 'std :: for_each' będzie lepszym rozwiązaniem, ponieważ staną się standardem, który (miejmy nadzieję) _all_ kompilatory ostatecznie będzie wspierać. – sbi

-2

Twoja klasa powinna dziedziczyć IEnumerable używać foreach