2012-10-24 9 views
32

मैं अपने आप को सी ++ 11 अधिक से अधिक हाल ही में उपयोग करते हुए पाते हैं, और मैं कहाँ होगा अतीत में iterators का उपयोग किया गया है, अब मैं range-based for loops उपयोग कर रहा हूँ कि जब भी संभव:मूल प्रकारों पर पुनरावृत्ति करते समय कॉन्स्ट संदर्भ का उपयोग करने का कोई नुकसान?

std::vector<int> coll(10); 
std::generate(coll.begin(), coll.end(), []() { return rand(); }); 

सी ++ 03:

for (std::vector<int>::const_iterator it = coll.begin(); it != coll.end(); ++it) { 
    foo_func(*it); 
} 

सी ++ 11:

for (auto e : coll) { foo_func(e); } 

लेकिन क्या होगा अगर संग्रह तत्व प्रकार टेम्पलेट पैरामीटर है?

foo_func(const BigType& e) { ... }; 
foo_func(int e) { ... }; 

मैं हार नहीं मानी इतना सोचा जब मैं सी ++ 03 उपयोग कर रहा था: foo_func() शायद मूल्य द्वारा स्थिरांक संदर्भ द्वारा जटिल (= कॉपी करने के लिए महंगा) प्रकार, और सरल लोगों पारित करने के लिए अतिभारित हो जाएगा उपरोक्त स्टाइल कोड। मैं वही तरीके से पुन: प्रयास करूंगा, और चूंकि कॉन्स्ट_इटरेटर को संदर्भित करने से एक कॉन्स्ट संदर्भ उत्पन्न होता है, सबकुछ ठीक था। लेकिन सी ++ 11 रेंज आधारित पाश के लिए, मैं एक ही व्यवहार प्राप्त करने के लिए एक स्थिरांक संदर्भ पाश चर का उपयोग करने की आवश्यकता का उपयोग कर:

for (const auto& e : coll) { foo_func(e); } 

, अगर यह परिचय नहीं होता और अब अचानक मुझे यकीन है कि नहीं था अनावश्यक असेंबली निर्देश अगर auto एक साधारण प्रकार था (जैसे संदर्भ को लागू करने के लिए पीछे के दृश्य सूचक)।

लेकिन एक नमूना अनुप्रयोग संकलन ने पुष्टि की कि सरल प्रकारों के लिए कोई ओवरहेड नहीं है, और यह टेम्पलेट्स में लूप के लिए श्रेणी-आधारित का उपयोग करने का सामान्य तरीका प्रतीत होता है। यदि यह मामला नहीं था, तो boost::call_traits::param_type जाने का रास्ता होता।

प्रश्न: क्या मानक में कोई गारंटी है?

(मुझे लगता है कि इस मुद्दे को वास्तव में छोरों के लिए सीमा के आधार पर करने के लिए संबंधित नहीं है। यह जब const_iterators का उपयोग कर भी मौजूद है।)

उत्तर

12

मानक कंटेनर उनके इटरेटर (ध्यान दें, तथापि से सभी वापसी संदर्भ, कुछ है कि " कंटेनर नहीं वास्तव में कंटेनर, जैसे, std::vector<bool> जो एक प्रॉक्सी रिटर्न) मानक प्रदर्शन के संबंध में कोई गारंटी नहीं है। अन्य iterators प्रॉक्सी या मान सकता है, हालांकि यह सख्ती से समर्थित नहीं है।

बेशक

, किसी भी तरह की प्रदर्शन से संबंधित सुविधा (जटिलता गारंटी से परे) को कार्यान्वयन की गुणवत्ता माना जाता है।

कहा कि, आप संकलक रखने पर विचार करना चाह सकते हैं आप के लिए विकल्प बनाने के रूप में यह पहले किया था:

for (auto&& e: coll) { f(e); } 

मुख्य यहां मुद्दा यह है कि f() गैर const संदर्भ प्राप्त हो सकता है है। coll के const संस्करण का उपयोग करके आवश्यक होने पर इसे रोका जा सकता है।

+1

या ... केवल 'ऑटो कॉन्स्ट &'. ;) – Xeo

+1

@Xeo का उपयोग करते हुए: यदि इटरेटर मूल्य के संदर्भ में ['const'] संदर्भ के बजाय मान प्राप्त करता है तो' T && 'संदर्भ के बजाय मान को घटाता है मूल्य। –

+0

* हू? * 'ऑटो और 'हमेशा एक संदर्भ होगा, इससे कोई फर्क नहीं पड़ता कि इटरेटर क्या उपज करते हैं। यदि यह वास्तव में एक मूल्य पैदा करता है, तो यह केवल एक रावल्यू संदर्भ होगा। (नोट: मैंने आपकी टिप्पणी को गलत समझा होगा।) – Xeo

11

6.5.4/1 का कहना है:

for (for-range-declaration : braced-init-list) statement 

देना रेंज-init braced-init-सूची के बराबर हो।प्रत्येक मामले में, एक रेंज आधारित बयान के लिए

{ 
    auto && __range = range-init; 
    for (auto __begin = begin-expr, 
       __end = end-expr; 
      __begin != __end; 
      ++__begin) { 
     for-range-declaration = *__begin; 
     statement 
    } 
} 

के बराबर है (अधिक विवरण कि सभी __ gubbins के अर्थ की इस प्रकार है)।

मानक किसी भी की गारंटी देता है चाहे वह लाइन const auto &e = *__begin, एक प्रदर्शन भूमि के ऊपर का परिचय ज़ाहिर है, सीधे *__begin बजाय e का उपयोग कर बयान अंदर के साथ तुलना नहीं है। कुछ स्टैक स्लॉट में श्रमिक रूप से एक पॉइंटर की प्रतिलिपि करके संदर्भों को कार्यान्वित करने के लिए कार्यान्वयन की अनुमति है और फिर प्रत्येक बार जब संदर्भ का उपयोग किया जाता है, तो उसे वापस पढ़ना और अनुकूलित करने की आवश्यकता नहीं होती है।

लेकिन वहाँ इस मामले में जहां __begin एक कंटेनर इटरेटर (जिसका operator* एक संदर्भ रिटर्न) है, और फिर eबयान में मूल्य से पारित हो जाता है में कोई कारण नहीं क्यों एक समझदार संकलक में एक ओवरहेड होना चाहिए है,।

+0

धन्यवाद स्टीव। तो आप लूप के लिए एक सामान्य श्रेणी-आधारित कैसे लिखेंगे? –

+0

@ डैनियल: मुझे लगता है कि '(कॉन्स ऑटो और ई: कॉल) {foo_func (e); } 'ठीक है, लेकिन मैंने सी ++ 11 का उपयोग नहीं किया है ताकि वह खुद के लिए एक स्टाइल गाइड लिखने में सक्षम हो सके जो इसे निर्देशित करता है। 'Boost :: call_traits :: param_type' के लिए उपयोग यह है कि एक बार जब आप पैरामीटर पास कर रहे हैं, तो एक छोटे ऑब्जेक्ट प्रकार के बजाय संदर्भ का उपयोग करने में संभावित ओवरहेड होता है, क्योंकि कॉलिंग कॉन्फ़्रेंस चीजों को प्रतिबंधित करता है। एक समारोह के भीतर मुझे लगता है कि मैं संदर्भ चर को "शुद्ध" उपनाम के रूप में मानने के लिए संकलक पर भरोसा करता हूं। यदि मूल्य से '* __ शुरू होता है 'तो मैं इसे जांच किए बिना अतिरिक्त अस्थायी से बचने पर शर्त नहीं लगाऊंगा। –

संबंधित मुद्दे