2013-01-22 42 views
11

मैं अनुक्रमों के कार्यात्मक शैली के निर्माण के लिए कुछ कोड लिखने की कोशिश कर रहा था। मैंने एक फ़ंक्शन, range(a, b) लिखा है, जो एक ऑब्जेक्ट देता है जिसे आप नंबर, ए + 1, ..., बी -1 के माध्यम से जाने के लिए फिर से शुरू कर सकते हैं। फिर मैंने एक और फ़ंक्शन लिखा, map(f, t), जो रिटर्न एक अन्य पुनरावर्तनीय वस्तु जहां अनुक्रम में प्रत्येक तत्व f को पुनरावृत्त ऑब्जेक्ट t के संबंधित तत्व के साथ कॉल करने का परिणाम है।जीसीसी मेरे कस्टम इटरेटर का उपयोग कर इस सी ++ 11 फोरैच लूप को ऑप्टिमाइज़ क्यों करता है?

यह अपेक्षा करता है कि यदि मैं -O1 या उससे कम का उपयोग कर संकलित करता हूं; -O2 या उच्चतम के साथ, मेरा फ़ोरैच लूप (नीचे main में) पूरी तरह से अनुकूलित हो जाता है और कुछ भी मुद्रित नहीं होता है। ऐसा क्यों होता है, मैंने क्या गलत किया है?

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

शायद एक बग? क्लैंग ++ के साथ, यह ओ 1 और ओ 2 अनुकूलन स्तर दोनों के साथ ठीक काम करता है। –

+7

'_map' को अपने सदस्यों को कॉन्स रेफरी संग्रहीत करने के बजाय मूल्य के आधार पर स्टोर करने का प्रयास करें। (मुझे संदेह है कि आपकी 'रेंज' ऑब्जेक्ट को उम्मीद से पहले नष्ट किया जा रहा है।) – ildjarn

+3

मेरा मानना ​​है कि @ildjarn इसे सही मिला: अस्थायी को तब तक जीने के लिए मजबूर होना पड़ता है जब तक निरंतर संदर्भ जीवित नहीं है। संदर्भ यह है कि यह 'मानचित्र' के निर्माता का तर्क है। जब कन्स्ट्रक्टर लौटाता है, तो संदर्भ दायरे से बाहर हो जाता है और अस्थायी नष्ट हो जाता है। –

उत्तर

8

मौजूदा टिप्पणियों का सारांश:

range(-4, 5) एक अस्थायी बनाता है, और (अधिकांश मामलों में) पूर्ण अभिव्यक्ति है जिसमें वे बनाई गई हैं के अंत तक केवल लाइव temporaries यहाँ मेरी कोड है। तो आपके मामले में, _range ऑब्जेक्ट _map के निर्माण के दौरान मान्य है, लेकिन जैसे ही कहा गया है _mapmap से वापस आ गया है, पूर्ण अभिव्यक्ति समाप्त होती है और _range ऑब्जेक्ट नष्ट हो जाता है।

जिसके अनुसार, क्योंकि _map तर्क स्थिरांक रेफरी के बजाय मूल्य द्वारा अपने निर्माता को पारित कर दिया रखती है, इसका मतलब है कि जब तक सीमा के आधार पर for को क्रियान्वित करने लगता है और आपके _map::t पहले से ही एक झूलने संदर्भ है - क्लासिक undefined behavior

इसे ठीक करने के लिए, बस _map अपने डेटा सदस्यों को मूल्य के अनुसार स्टोर करें।