2013-01-22 12 views
11

Stavo cercando di scrivere del codice per la creazione di sequenze in stile funzionale. Ho scritto una funzione, range(a, b), che restituisce un oggetto che è possibile scorrere sopra, in stile foreach, per passare attraverso i numeri a, a + 1, ..., b - 1. Quindi ho scritto un'altra funzione, map(f, t), che restituisce un altro oggetto iterabile in cui ogni elemento della sequenza è il risultato della chiamata f con l'elemento corrispondente dell'oggetto iterable t.Perché gcc ottimizza questo ciclo foreach C++ 11 usando il mio iteratore personalizzato?

Funziona come previsto se compilo utilizzando -O1 o inferiore; con -O2 o superiore, il mio ciclo foreach (in main nella parte inferiore) viene completamente ottimizzato e non viene stampato nulla. Perché succede, cosa ho fatto di sbagliato? Qui è il mio codice:

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

Forse un errore? Con Clang ++, funziona bene con entrambi i livelli di ottimizzazione O1 e O2. –

+7

Prova ad avere '_map' per memorizzare i suoi membri in base al valore invece di memorizzare i riferimenti const. (Sospetto che il tuo oggetto 'range' sia stato distrutto prima di quanto speravi.) – ildjarn

+3

credo che @ildjarn abbia capito bene: il temporaneo è costretto a vivere finché il riferimento costante a cui è legato è vivo. il riferimento a cui è vincolato è l'argomento del costruttore di 'map'. quando il costruttore ritorna, il riferimento esce dall'ambito e il temporaneo viene distrutto. –

risposta

8

Riassumendo i commenti esistenti:

range(-4, 5) crea una temporanea e (in molti casi) provvisori solo dal vivo fino alla fine del full-espressione in cui sono stati creati. Nel tuo caso, l'oggetto restituito _range è valido durante la costruzione dello _map, ma non appena lo _map viene restituito da map, l'espressione completa termina e l'oggetto _range viene distrutto.

Ciò detto, perché _map tiene gli argomenti passati al suo costruttore da ref const piuttosto che per valore, questo significa che per il momento la gamma a base di for inizia l'esecuzione, la vostra _map::t è già un punto di riferimento penzoloni - classico undefined behavior.

Per risolvere questo problema, è sufficiente che _map memorizzi i suoi membri dati in base al valore.

Problemi correlati