2013-01-22 3 views
11

Ich habe versucht, einen Code für die funktionale Erstellung von Sequenzen zu schreiben. Ich schrieb eine Funktion, range(a, b), die ein Objekt zurückgibt, das iteriert werden kann, foreach-style, durch die Zahlen a, a + 1, ..., b - 1. Dann schrieb ich eine andere Funktion, map(f, t), die zurückgibt ein anderes iterierbares Objekt, bei dem jedes Element in der Sequenz das Ergebnis des Aufrufs von f mit dem entsprechenden Element des iterierbaren Objekts t ist.Warum optimiert gcc diese C++ 11 foreach-Schleife mit meinem benutzerdefinierten Iterator?

Dies funktioniert wie erwartet, wenn ich kompilieren mit -O1 oder niedriger; mit -O2 oder höher, meine foreach loop (in main an der unterseite) wird komplett weg optimiert und nichts ist gedruckt. Warum passiert das, was habe ich falsch gemacht? Hier ist mein Code:

template<typename T> 
struct _range { 
    T a; 
    T b; 

    _range(T a, T b): 
     a(a), 
     b(b) 
    { 
    } 

    struct iterator { 
     T it; 

     iterator(T it): 
      it(it) 
     { 
     } 

     bool operator!=(const iterator &other) const 
     { 
      return it != other.it; 
     } 

     void operator++() 
     { 
      ++it; 
     } 

     T operator*() const 
     { 
      return it; 
     } 
    }; 

    iterator begin() const 
    { 
     return iterator(a); 
    } 

    iterator end() const 
    { 
     return iterator(b); 
    } 
}; 

template<typename T> 
_range<T> range(const T a, const T b) 
{ 
    return _range<T>(a, b); 
} 

template<typename F, typename T> 
struct _map { 
    const F &f; 
    const T &t; 

    _map(const F &f, const T &t): 
     f(f), 
     t(t) 
    { 
    } 

    struct iterator { 
     const F &f; 
     typename T::iterator it; 

     iterator(const F &f, typename T::iterator it): 
      f(f), 
      it(it) 
     { 
     } 

     bool operator!=(const iterator &other) const 
     { 
      return it != other.it; 
     } 

     void operator++() 
     { 
      ++it; 
     } 

     int operator*() const 
     { 
      return f(*it); 
     } 
    }; 

    iterator begin() const 
    { 
     return iterator(f, t.begin()); 
    } 

    iterator end() const 
    { 
     return iterator(f, t.end()); 
    } 
}; 

template<typename F, typename T> 
_map<F, T> map(const F &f, const T &t) 
{ 
    return _map<F, T>(f, t); 
} 

#include <algorithm> 
#include <cstdio> 

int main(int argc, char *argv[]) 
{ 
    for (int i: map([] (int x) { return 3 * x; }, range(-4, 5))) 
     printf("%d\n", i); 

    return 0; 
} 
+0

Vielleicht ein Fehler? Mit Clang ++ funktioniert es sowohl bei den O1- als auch bei den O2-Optimierungsebenen. –

+7

Versuchen Sie, '_map' speichern seine Mitglieder nach Wert anstatt const refs zu speichern. (Ich vermute, dass dein "Entfernungs" -Objekt früher zerstört wird, als du gehofft hast.) – ildjarn

+3

Ich glaube, @ildjarn hat es richtig verstanden: Das Temporäre ist gezwungen zu leben, solange die konstante Referenz, an die es gebunden ist, am Leben ist. Die Referenz, an die es gebunden ist, ist das Argument des Konstruktors von map. Wenn der Konstruktor zurückkehrt, wird der Verweis ungültig und das temporäre Objekt wird gelöscht. –

Antwort

8

Fasst man die bestehenden Kommentare:

range(-4, 5) erstellt eine temporäre und (in den meisten Fällen) Provisorien nur leben, bis zum Ende der Voll Ausdruck, in dem sie erstellt werden. In diesem Fall ist also das zurückgegebene _range Objekt während der Konstruktion des _map gültig, aber sobald _map von map zurückgegeben wird, endet der vollständige Ausdruck und das Objekt _range wird zerstört.

Das heißt, weil _map die Argumente an den Konstruktor von const ref anstatt von Wert übergeben hält, bedeutet dies, dass die bereichsbasierte durch die Zeit for beginnt mit der Ausführung, Ihre _map::t ist bereits ein baumelnden Referenz - klassische undefined behavior.

Um dies zu beheben, einfach _map seine Datenelemente nach Wert speichern.

Verwandte Themen