range based for-loop : wie kommt man an den Index?



  • Jeder Vektor sollte doch auch einen Iterator haben, den man anfordern kann.
    Mit diesem Iterator hat man dann auch den Index.



  • MC78 schrieb:

    Jeder Vektor sollte doch auch einen Iterator haben, den man anfordern kann.
    Mit diesem Iterator hat man dann auch den Index.

    Wie sähe das z. B. aus?


  • Mod

    Das ist schnell geschrieben:

    template <typename C>
    boost::integer_range<typename C::size_type> keys( C const& c )
    {
    	return {0, c.size()};
    }
    
    template <typename C, std::size_t N>
    boost::integer_range<C> keys( C (&arr)[N] )
    {
    	return {0, N};
    }
    

    Übrigens habe ich gerade einen lust'gen Bug bei Clang entdeckt, bei folgendem Code

    #include <boost/range/irange.hpp>
    
    template <typename C>
    auto keys( C const& c ) -> boost::irange<typename C::size_type>
    {
    	return {0, C.size()};
    }
    
    int main()
    {
    	int arr[3];
    	keys(arr);
    }
    

    Der ist so lustig, weil er nur gelegentlich auftritt. Kompiliert das mal zehn mal hintereinander, und seht, wie oft Clang abkrazt. Bei mir ist es ca. 4 mal.



  • Ich finde for-range im Moment nur bedingt einsatzfähig. Ich würde es gern für viel mehr Fälle einsetzen.

    Meine Hoffnung ist, dass spätestens bei C++17 dann auch Hilfsmittel in der Art von Boost-Range-Adaptern dabei sind, über die ich z.B. einen Index oder einen "Reißverschluss" bekomme:

    Beispiel für das Mitzählen

    vector<double> x;
    for (auto t : counted(x)) {
       // t könnte hier ein tuple<decltype(x.size()),double&> sein
       cout << "Der " << (1+get<0>(t)) << ". Eintrag ist "
            << get<1>(t) << endl;
    }
    

    Beispiel für zip

    vector<double> vecx;
    vector<double> vecy;
    for (auto t : zip(vecx,vecy)) {
       // t könnte hier ein tuple<double&,double&> sein
       cout << get<0>(t) << ", " << get<1>(t) << endl;
    }
    

    ...wobei ich mir noch nicht im Klaren darüber bin, ob man hier wirklich "Referenz-Tupel" will oder nicht. Ich favorisiere T = decltype((*iterator)) für tuple<...,T,...> , denke ich.

    Und wer sich hier jetzt hat inspirieren lassen und wegen Langeweile so etwas basteln will, könnte auch noch hier für noch mehr Inspiration schauen.



  • Es wurde richtig bemerkt, dass man mit static vorsichtig sein soll, weil das Programmstück nur beim ersten und einzigen Durchlauf funktioniert. Der Grund ist ja der, dass solche Variablen nicht auf dem Stack angelegt werden, sondern zu Start des Programms irgendwo im Freispeicher und dabei auch inititalisiert werden und somit bei Wiedereintritt in den Block keine weitere Initialisierung auftritt.

    Wenn garantiert ist, dass der Block nur ein einziges Mal ausgeführt wird, ist das ok, z.B. wenn der Block kein Funktionskörper ist, sondern einfach so im Hauptprogramm steht, wie das bei einer Initialisierung sein kann.

    Verwendet man z. B. ein Funktionsobjekt, dass den Index mitzählen und die Berechnungen durchführen soll, so tritt genau dieser Effekt auch ein, dass nämlich bei der Konstruktion des Objektes ein einziges Mal inititalisiert wird und sonst nie wieder. Man muss also auch hier ggf. die Lebensdauer des Objektes begrenzen, damit bei Wiederholung kein Unsinn passiert.

    {
    for(auto & x : X)
       {
       static unsigned i = 0;
       x = somefunc(i++);
       }
    }
    

    ist also ähnlich wie:

    class funcobj
       { 
       public: 
       funcobj():i(0){};
       float operator()(){return somefunc(i++);};
       private:
       unsiged i;
       };
    
    ...
    
    funcobj gen;
    generate(X.begin(),X.end(),gen);
    generate(X.begin(),X.end(),gen);  //??
    

    Es ist ganz interessant, dass in Büchern steht, man nehme halt ein Funktionsobjekt, aber über die Wiederholung steht da nichts.



  • Die Laufzeiten sind auch ganz interessant.
    Es scheint so, als haben die Lösungen mit range-based-for und generate die Nase weit vorne, mit ca. nur 30% der Zeit, die die klassische for-Schleife mit oder ohne Iteratoren braucht. Natürlich ist das bei size() = 100 uninteressant. Aber ich nehme ja C++, weil die Problemgröße interessant ist, also eher size() > 1e8.



  • wastl schrieb:

    Es ist ganz interessant, dass in Büchern steht, man nehme halt ein Funktionsobjekt, aber über die Wiederholung steht da nichts.

    Dann erzeugt man eben ein neues.



  • kkaw schrieb:

    Beispiel für das Mitzählen

    vector<double> x;
    for (auto t : counted(x)) {
       // t könnte hier ein tuple<decltype(x.size()),double&> sein
       cout << "Der " << (1+get<0>(t)) << ". Eintrag ist "
            << get<1>(t) << endl;
    }
    

    Das ist aber etwas unschön mit dem Tupel. Lieber ein Typ mit verständlich benannten Elementen:

    vector<double> x;
    for (auto t : counted(x)) {
       // t könnte hier ein tuple<decltype(x.size()),double&> sein
       cout << "Der " << (1+t.index) << ". Eintrag ist "
            << t.value << endl;
    }
    


  • ➡

    vector<double> x{1.1,2.2,3.3,4.4,5.5,6.6};
    auto indexed = x | boost::adaptors::indexed(1);
    for (auto t : boost::irange(begin(indexed), end(indexed)))
        cout << "Der " << t.index() << ". Eintrag ist "
             << *t << endl;
    


  • TyRoXx schrieb:

    wastl schrieb:

    Es ist ganz interessant, dass in Büchern steht, man nehme halt ein Funktionsobjekt, aber über die Wiederholung steht da nichts.

    Dann erzeugt man eben ein neues.

    Genau. Ich habe es nur erwähnt, weil sich Leute über ein Beispiel furchtbar aufgeregt haben, dass man ebenso betitteln könnte: einmal ausführren, wenn zweimal, dann Block erneut hinschreiben (man braucht eine Inititalisierung nur einmal, aber egal...)



  • Ich benutze dafür ein Makro:

    foreach_i(x,X)x*=i;
    

    Quadratisch, praktisch, gut.

    Passend gibt es auch ein foreach_j für den Fall der Fälle.



  • arens schrieb:

    Ich benutze dafür ein Makro:

    foreach_i(x,X)x*=i;
    

    Quadratisch, praktisch, gut.

    Passend gibt es auch ein foreach_j für den Fall der Fälle.

    So viel kürzer als

    for (auto it : with_index(x))
        *it *= it.index;
    

    ist das auch nicht.



  • boost based for loop schrieb:

    ➡

    vector<double> x{1.1,2.2,3.3,4.4,5.5,6.6};
    auto indexed = x | boost::adaptors::indexed(1);
    for (auto t : boost::irange(begin(indexed), end(indexed)))
        cout << "Der " << t.index() << ". Eintrag ist "
             << *t << endl;
    

    Sehr nett! Wusste gar nicht, dass man irange auch so verwenden kann. Dachte, ich bekomme damit nur Ganzzahl-Sequenzen hin. Aber das hier ist ja eine Sequenz von Iteratoren. Scheint laut Doku nicht vorgesehen und eher ein "Unfall" zu sein.


Anmelden zum Antworten