2012-03-19 13 views
19

https://doc-snapshots.qt.io/qtcreator-extending/coding-style.html पर के लिए एक महंगा आपरेशन हो तो वह ऐसा छोरों के लिए लिखने के लिए सिफारिश की है:समाप्त कर सकते हैं() STL कंटेनर

Container::iterator end = large.end(); 
for (Container::iterator it = large.begin(); it != end; ++it) { 
     //...; 
} 

for (Container::iterator it = large.begin(); it != large.end(); ++it) { 
     //...; 
} 

के बजाय

जब से मैं शायद ही कभी इस शैली को देखा है किसी भी कोड में, मैं जानना चाहता हूं कि अंत() की निरंतर कॉल वास्तव में एसएलएल कंटेनरों पर बड़े लूप के लिए एक नोटिस करने योग्य रन-टाइम ओवरहेड जोड़ती है या क्या संकलक पहले से ही ऐसे मामलों को अनुकूलित करते हैं।

संपादित करें: बहुत अच्छा टिप्पणी करने के लिए के रूप में कई ने कहा: यह सवाल ही मान्य है, तो पाश अंदर कोड अंत इटरेटर संशोधित नहीं करता है। अन्यथा निश्चित रूप से अंत की बार-बार कॉल अनिवार्य है।

+12

के रूप में एक अलग रूप में: मैं भी 'के लिए (कंटेनर :: इटरेटर के लिए जाना चाहते हैं के रूप में प्रति cppreference.com

, सीमा आधारित पाश के लिए निम्नलिखित के रूप में एक ही साइड इफेक्ट के साथ कोड का उत्पादन करेगा यह = big.begin(), end = big.end(); it! = end; ++ it) {...} 'चर के लिए' अंत 'के लिए दायरे को सीमित करने के लिए केवल फॉर-लूप तक सीमित करें। – haffax

+0

सी # और जावा देव इस तरह के लूप लिखते हैं ताकि जेआईटीआर इसे अनुकूलित कर सके (प्रति पुनरावृत्ति प्रति एक चेक)। ऐसा लगता है कि सी ++ के मामले में नहीं है। –

+4

सी ++ देव सिर्फ 'for_each (शुरू करें (सी), अंत (सी), []() {});' लूप्स लाइब्रेरी लेखकों के लिए हैं: पी – MSalters

उत्तर

17

सी ++ 11 मानक (§ 23.2.1) अनिवार्य है कि end में ओ (1) जटिलता है, इसलिए एक अनुरूप कार्यान्वयन में दोनों संस्करणों के लिए समान प्रदर्शन विशेषताएं होंगी।

यही कारण है, ने कहा कि जब तक संकलक साबित कर सकते हैं कि end के रिटर्न मान तो कभी नहीं बदलेगा end लूप से बाहर खींच तेजी से कुछ निरंतर मात्रा से हो सकता है (के रूप में स्टीव जेसप टिप्पणी, वहाँ चर के बहुत सारे है कि कर सकते हैं प्रभावित करें कि यह सच है या नहीं)।

फिर भी, अगर एक विशेष मामले में बिल्कुल कोई प्रदर्शन अंतर नहीं है, तो लूप के बाहर ऐसे परीक्षण खींचने की अच्छी आदत है। एक भी बेहतर आदत में प्रवेश करने के लिए मानक एल्गोरिदम का उपयोग करना है @pmr कहते हैं, जो पूरी तरह से इस मुद्दे को दूर करता है।

+7

के लिए नहीं हैं, भले ही कंपाइलर साबित न कर सके कि 'एंड' का मान लूप-इनवेरिएंट है, फिर भी इसे उछालना कोई तेज़ नहीं हो सकता है। इसका कारण यह है कि अगर इसे फहराया जाता है, तो भी मान को एक चर में संग्रहीत किया जाना चाहिए, जो ढेर पर हो सकता है। कंटेनर भी ढेर पर हो सकता है, जिस स्थिति में कंटेनर में कुछ डेटा सदस्य से अपना 'अंत' पढ़ना संभवतः एक ही ऑपरेशन के रूप में एक ही चर के बाहर 'अंत' को पढ़ने के समान ही ऑपरेशन होने की संभावना है। यदि कंटेनर संदर्भ द्वारा पारित किया गया है, तो एक अतिरिक्त संकेत हो सकता है और यह थोड़ा धीमा हो सकता है। –

+0

@SteveJessop: अंतर्दृष्टिपूर्ण टिप्पणी के लिए धन्यवाद, मैंने शब्द बदल दिया ताकि यह गलत इंप्रेशन न छोड़ सके। इसके अलावा, आईएमएचओ हम (इंसान) वास्तव में इस इतनी पतली विच्छेदन नहीं करनी चाहिए क्योंकि यह बेहद असंभव है कि इस तरह की छोटी परिमाण के प्रदर्शन अंतर की भविष्यवाणी की जा सकती है। – Jon

+2

मैं मानता हूं कि हम इन छोटे मतभेदों की भविष्यवाणी नहीं कर सकते हैं। मैं वास्तव में सहमत नहीं हूं कि इस तरह के परीक्षण उठाना एक अच्छी आदत है। कुछ लोगों के लिए यह पठनीयता में सुधार कर सकता है (1 से अधिक की बजाय 2 छोटी लाइनें), अन्यथा यह एक सट्टा अनुकूलन है और वास्तव में आईएमओ के लिए कोड को खराब करने के लायक नहीं है। अगर उछाल पूरे ऑपरेशन की जटिलता को कम कर देता है, या यदि यह स्पष्ट रूप से लूप में किए गए काम का एक महत्वपूर्ण अनुपात होने जा रहा है, तो मैं इसे अनुमान लगाऊंगा। अन्यथा मैं नहीं करता, हालांकि मैं एल्गोरिदम या श्रेणी-आधारित का उपयोग करते समय इसे "मुक्त" करने के लिए भी ऑब्जेक्ट नहीं करता। –

-3

कार्यान्वयन पर निर्भर करता है, लेकिन मुझे नहीं लगता कि end() उस प्रदर्शन का एक बड़ा हिस्सा देता है।

3

यदि आप संग्रह को संशोधित करने की योजना बनाते हैं, तो आपको इसे दूसरे तरीके से करना होगा (अंत बदल सकता है) - अन्यथा सैद्धांतिक रूप से एक अंश तेजी से होता है। मुझे संदेह है कि यह ध्यान देने योग्य होगा हालांकि।

7

यह कम के बारे में end महंगा और एक संकलक की क्षमता को देखने के लिए कि end पाश शरीर में एक पक्ष प्रभाव के माध्यम से नहीं बदलेगी (है कि यह एक पाश अपरिवर्तनीय है) के बारे में अधिक किया जा रहा है।

end की जटिलता मानक द्वारा स्थिर होने की आवश्यकता है। 23.2.1 में N3337 में तालिका 96 देखें।

मानक लाइब्रेरी एल्गोरिदम का उपयोग पूरी दुविधा को अच्छी तरह से रोकता है।

2

वास्तव में, अंत() विधि इनलाइन है। दूसरा इसे हर बार कॉल नहीं करता है, मुझे नहीं लगता कि अंत() कोई प्रदर्शन अंतराल देता है।

0

std :: vector.end() (for example) मूल्य द्वारा एक पुनरावर्तक लौटाएं। दूसरे पाश में आप प्रत्येक लूप पर एक ऑब्जेक्ट बनाते हैं। कोडिंग मानक आपको ऑब्जेक्ट बनाने के लिए कह रहा है अगर आपको इसकी आवश्यकता नहीं है। संकलक आपके लिए कोड स्मार्ट और अनुकूलित कर सकता है, हालांकि यह गारंटी नहीं है। एक बेहतर समाधान एसएलएल एल्गोरिदम का उपयोग कर रहा है। वे पहले से ही अनुकूलित हैं और आपका कोड अधिक पठनीय होगा। सावधान रहें कि दो लूप केवल तभी समकक्ष हैं जब आप संग्रह को संशोधित नहीं करते हैं।

पीएसयह बहुत संभावना है कि प्रदर्शन में अंतर बहुत कम है

+1

'end()' पर कॉल की संभावना है, जैसा कि 'ऑपरेटर' = 'कॉल' है। उस स्तर पर हम दो पॉइंटर्स की तुलना कर रहे हैं। उनमें से एक की एक अलग प्रति होने के प्रदर्शन में वृद्धि की संभावना नहीं है। –

+0

मैं मानता हूं कि प्रदर्शन में अंतिम परिणाम बहुत अलग नहीं होगा, हालांकि मुझे ऑब्जेक्ट प्रसार का ध्यान देना पसंद है। –

0

अब इसे पढ़ने के लिए, सवाल सी ++ 11 के साथ एक महत्वपूर्ण बिंदु बन गया है।

मुझे यकीन नहीं था कि यह प्रतिक्रिया उत्तर के रूप में योग्य है या नहीं, क्योंकि यह वास्तव में प्रश्न के बिंदु को संबोधित नहीं करती है। लेकिन मुझे लगता है कि यह इंगित करने के लिए मान्य है कि यहां उठाई गई समस्या को कभी-कभी सी ++ 11 प्रोग्रामर के अभ्यास में सामना करना पड़ेगा, और मुझे निश्चित रूप से कुछ साल पहले यह प्रतिक्रिया उपयोगी लगेगी। इसलिए यह प्रतिक्रिया पाठक के लिए लक्षित है जो एसटीएल कंटेनर (vector, list, deque, आदि) के सभी तत्वों के माध्यम से सर्वोत्तम को फिर से शुरू करने का तरीका जानना चाहता है।

Container container; // my STL container that has been filled with stuff 

// (note that you can replace Container::value_type with the value in the container) 

// the standard way 
for (Container::value_type element : container) { 
    // access each element by 'element' rather than by '*it' 
} 

// or, if Container::value_type is large 
Container container; // fill it with something 
for (Container::value_type& element : container) { 
    // 
} 

// if you're lazy 
Container container; // fill it with something 
for (auto element : container) { 
    // 
} 

ओपी से कहा है कि:

यह मानते हुए कि ओपी कंटेनर में प्रत्येक तत्व के लिए उपयोग करना चाहता था, हम आसानी से है कि क्या परिभाषित करने end पर्याप्त तेजी से होता है लेखन एक range-based for loop द्वारा Container::end() बुला से की पूरी सवाल से बचने कर सकते हैं क्या प्रत्येक पुनरावृत्ति पर it से Container::end() की तुलना करने की ब्रेवटी के बीच व्यापार-बंद और परिवर्तनीय end घोषित करने के प्रदर्शन और इसके बजाय प्रत्येक चरण में इसकी तुलना करना इसके लायक है। चूंकि लूप के लिए रेंज-आधारित एक सरल, आसान लिखना आसान है और आसानी से पढ़ने के लिए आसान है, आंतरिक रूप से, प्रत्येक चरण में Container::end() विधि को कॉल करने के बजाय end इटेटरेटर घोषित करता है, ऐसे मामलों की संख्या जहां हमें इस पर रहने की आवश्यकता है प्रश्न सीमित मामलों में सीमित कर दिया गया है।

{ 
    auto && __range = range_expression ; 
    for (auto __begin = begin_expr, 
     __end = end_expr; 
     __begin != __end; ++__begin) { 
    range_declaration = *__begin; 
    loop_statement 
    } 
} 
संबंधित मुद्दे