2014-09-24 28 views
5

यदि आप मूल्य प्रकारों के साथ C++ std :: map (और अन्य कंटेनर) का उपयोग करते हैं, तो आप देखेंगे कि मानचित्र में डालने से आपके तत्व प्रकार के लिए विनाशक को कॉल किया जाता है।क्यों सी ++ std :: map :: ऑपरेटर [] जगह जगह का उपयोग नहीं करता है?

(*((std::map<>::insert(std::make_pair(x, T()))).first)).second 

यह आदेश है कि जोड़ी बनाने के लिए आपके प्रकार के डिफ़ॉल्ट निर्माता कहता है: यह इसलिए है क्योंकि ऑपरेटर के कार्यान्वयन [] सी ++ कल्पना के लिए आवश्यक है इस के लिए बराबर हो रहा है। उस अस्थायी मूल्य को मानचित्र में कॉपी किया जाता है, और फिर नष्ट कर दिया जाता है। इसकी पुष्टि this stackoverflow post और here on codeguru में पाई जा सकती है।

मुझे जो अजीब लगता है वह यह है कि इसे अस्थायी चर की आवश्यकता के बिना लागू किया जा सकता है और फिर भी समकक्ष हो सकता है। सी ++ की एक विशेषता है जिसे "inplace new" कहा जाता है। Std :: map और अन्य कंटेनर ऑब्जेक्ट के लिए खाली स्थान आवंटित कर सकते हैं और फिर आवंटित स्थान पर तत्व के डिफ़ॉल्ट कन्स्ट्रक्टर को स्पष्ट रूप से कॉल कर सकते हैं।

मेरा प्रश्न: मैं इस ऑपरेशन को अनुकूलित करने के लिए नए स्थान का उपयोग करने वाले std :: मानचित्र के कार्यान्वयन में से कोई भी क्यों नहीं उपयोग करता हूं? ऐसा लगता है कि यह इस निम्न स्तरीय संचालन के प्रदर्शन में काफी सुधार करेगा। लेकिन कई आंखों ने एसटीएल कोड बेस का अध्ययन किया है, इसलिए मुझे लगता है कि ऐसा कुछ कारण होना चाहिए।

+0

टेस्ट केस लिंक किए गए स्टैक ओवरफ्लो पोस्ट में शामिल है। यहां लिंक फिर से दिया गया है: https://stackoverflow.com/questions/4017892/in-an-stl-map-of-structs-why-does-the-operator-cause-the-structs-dtor-to – srm

+0

माइक सेमुर: तो आप सुझाव दे रहे हैं कि डीबगर बंद होने के साथ, विनाशक को ये अतिरिक्त कॉल दूर चले जाएंगे? क्या आप जानते हैं कि यह सच है? भले ही यह सच है, इसका मतलब यह है कि डीबगिंग के साथ, निष्पादन को डीबग करना कठिन होता है (मैं अपने डीबगर में "वास्तविक" स्कोप मुद्दों की तलाश में केवल ब्रेकपॉइंट सेट नहीं कर सकता)। यह डिबगिंग के दौरान भी कम प्रदर्शनकारी है, जो एक कम मुद्दा है, लेकिन फिर भी वास्तविक है। इन दोनों कारणों से मुझे निम्न स्तर की लाइब्रेरी के बजाय नए स्थान का उपयोग करने के कारण होने लगते हैं। – srm

उत्तर

4

सामान्य रूप से, आप निम्न स्तर के संदर्भ में [] जैसे उच्च स्तरीय संचालन को निर्दिष्ट करना एक अच्छा विचार है।

सी ++ 11 से पहले, [] के साथ ऐसा करने से insert का उपयोग किए बिना मुश्किल होगी।

सी ++ 11 में, std::map<?>::emplace के अतिरिक्त और std::pair के लिए समान सामग्री हमें उस समस्या से बचने की क्षमता देता है। यदि आपने इसे फिर से परिभाषित किया है तो इस तरह के जगह निर्माण का उपयोग करें, अतिरिक्त (उम्मीदवार elided) वस्तु निर्माण दूर जाना होगा।

मैं ऐसा करने का कोई कारण नहीं सोच सकता। मैं आपको मानकीकरण के लिए प्रस्ताव देने के लिए प्रोत्साहित करता हूं।

एक std::map में कॉपी-कम प्रविष्टि प्रदर्शित करने के लिए, हम निम्नलिखित कर सकते हैं:

#include <map> 
#include <iostream> 

struct no_copy_type { 
    no_copy_type(no_copy_type const&)=delete; 
    no_copy_type(double) {} 
    ~no_copy_type() { std::cout << "destroyed\n"; } 
}; 
int main() { 
    std::map< int, no_copy_type > m; 
    m.emplace(
    std::piecewise_construct, 
    std::forward_as_tuple(1), 
    std::forward_as_tuple(3.14) 
); 
    std::cout << "destroy happens next:\n"; 
} 

live example - जैसा कि आप देख सकते हैं, कोई अस्थायी उत्पन्न होता है।

तो हम

(*((std::map<>::insert(std::make_pair(x, T()))).first)).second 

की जगह अगर

(* 
    (
    (
     std::map<>::emplace(
     std::piecewise_construct, 
     std::forward_as_tuple(std::forward<X>(x)), 
     std::forward_as_tuple() 
    ) 
).first 
).second 

के साथ कोई अस्थायी बनाई किया जाएगा (सफेद स्थान को मैं () रों का ट्रैक रखने के सकता है, ताकि जोड़ा)।

+0

माइक सेमुर से पहले के जवाब से पता चलता है कि वैकल्पिक आवंटकों को चुनने के लिए प्रोग्रामर की क्षमता का अर्थ है कि अस्थायी के निर्माण को कोड से हटाया नहीं जा सकता है और इसे अनुकूलन के दौरान elide किया जाना चाहिए, जो एक डिबगिंग दृष्टिकोण से दुखी है और समझाते हुए नए उपयोगकर्ताओं को डालने के दौरान एक विनाशक क्यों बुलाया जाता है। – srm

+0

@ एसआरएम निश्चित रूप से। लेकिन माइक गलत है। मानक में यह मामूली दोष (अक्षमता) है, मानते हुए कि यह पहले से तय नहीं हुआ था (मैंने नवीनतम जांच नहीं की थी)। आवंटकों को रिबाउंड किया जा सकता है, और प्रतिस्थापन और टुकड़े टुकड़े के निर्माण से आप बिना किसी अस्थायी वस्तु का निर्माण कर सकते हैं। एक खाली 'tuple' के माध्यम से डिफ़ॉल्ट निर्माण किया जा सकता है। – Yakk

+0

मुझे टिप्पणी करने के लिए पर्याप्त जानकारी नहीं है। यही कारण है कि मैंने सवाल पूछा। क्या आप अपनी टिप्पणी माइक के जवाब पर पोस्ट कर सकते हैं ताकि उसे अधिसूचना मिल सके और देखें कि क्या आप दो एक दूसरे को मनाने के लिए कर सकते हैं? – srm

0

पहले, std::map के लिए operator[<key>] केवल एक डालने आपरेशन अगर अनुरोध किया <key> नहीं पाया जाता है के बराबर है। इस मामले में, केवल कुंजी का संदर्भ आवश्यक है और केवल संग्रहित मूल्य का संदर्भ है।

दूसरा, जब नया तत्व डाला जाता है तो यह जानने का कोई तरीका नहीं है कि एक प्रतिलिपि का पालन किया जाएगा या नहीं। आपके पास map[_k] = _v; हो सकता है, या आपके पास _v = map[_k]; हो सकता है।पाठ्यक्रम के उत्तरार्ध में वही आवश्यकताएं होती हैं जैसे यह असाइनमेंट के बाहर होती है, यानी map[_k].method_call();, लेकिन प्रतिलिपि बनाने वाले (इसका निर्माण करने के लिए कोई स्रोत नहीं है) का उपयोग करें। सम्मिलन के संबंध में, उपरोक्त सभी की आवश्यकता है कि value_type के डिफ़ॉल्ट कन्स्ट्रक्टर को बुलाया जाए और उस स्थान को इसके लिए आवंटित किया जाए। भले ही हम operator[] लिखते समय जान सकें कि हम असाइनमेंट उपयोग-मामले में थे, हम संचालन के आदेश के कारण "इनस्थल नया" का उपयोग नहीं कर सके। value_type कन्स्ट्रक्टर को पहले कहा जाना चाहिए, उसके बाद value_type::operator=, जिसके लिए कॉपी कन्स्ट्रक्टर की कॉलिंग की आवश्यकता होती है।

हालांकि अच्छी सोच है।

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