2014-05-20 11 views
11

नोट: मूल रूप से Matt McnabbcommentWhy can swapping standard library containers be problematic in C++11 (involving allocators)? पर पूछा गया था।आवंटकों में 'propagate_on_container_swap == false` क्यों अनुमति दें, जब यह अपरिभाषित व्यवहार का कारण बन सकता है?


मानक (N3797) का कहना है कि अगर progagate_on_container_swap एक अंदर संभाजकstd::false_type है यह अपरिभाषित व्यवहार निकलेगा अगर शामिल दो allocators बराबर की तुलना नहीं करता है।

  • मानक इस तरह के निर्माण को खतरनाक से अधिक क्यों लगता है?

23.2.1p9जनरल कंटेनर आवश्यकताओं[container.requirements.general]

तो allocator_traits<allocator_type>::propagate_on_container_swap::value true, तो a और b की allocators भी गैर के लिए एक unqalified कॉल का उपयोग कर विमर्श किया जाएगा है सदस्य swapअन्यथा, वे स्वैप नहीं किए जाएंगे, और व्यवहार a.get_allocator() == b.get_allocator() तक अपरिभाषित है।

उत्तर

10

मैं कुछ वास्तविक जीवन स्थितियों जहां निर्माण स्टैंडर्ड द्वारा अनुमति दोनों समझ में आता है, और आवश्यक है, तथापि के बारे में सोच सकते हैं; मैं पहले इस प्रश्न का उत्तर व्यापक परिप्रेक्ष्य से करने का प्रयास करूंगा, जिसमें कोई विशिष्ट समस्या शामिल नहीं है।


व्याख्या

Allocators इस जादुई बातें, आवंटन का निर्माण, destructing, और स्मृति और संस्थाओं deallocating लिए जिम्मेदार हैं। सी ++ 11 के बाद से राज्य के आवंटकों खेलने में आया एक आवंटक पहले से कहीं अधिक कर सकता है, लेकिन यह पहले उल्लिखित चार परिचालनों तक उबाल जाता है।

Allocators आवश्यकताओं का भार है, उनमें से एक यह है कि a1 == a2 (जहां a1 और a2 एक ही प्रकार के allocators कर रहे हैं) उपज चाहिए trueकेवल अगर स्मृति एक करके आवंटित किया जा सकता है द्वारा पुनः आवंटित की जाती अन्य [1]

operator== की उपरोक्त आवश्यकता का अर्थ है कि बराबर की तुलना में दो आवंटक अलग-अलग काम कर सकते हैं, जब तक कि उन्हें अभी भी स्मृति आवंटित करने की आपसी समझ हो।

उपर्युक्त मानक propagate_on_container_*std::false_type के बराबर होने की अनुमति देता है; हम दो कंटेनरों की सामग्री को बदलना चाहते हैं जो आवंटकों के पास एक ही विलोपन व्यवहार होता है, लेकिन अन्य व्यवहार (मूल स्मृति प्रबंधन से संबंधित नहीं) को पीछे छोड़ दें।


[1] रूप [allocator.requirements]p2 में कहा गया है (तालिका 28)


(मूर्ख) कहानी

कल्पना कीजिए कि हम एक संभाजक है नाम वाटरिकेटर, यह अनुरोधित आवंटन पर पानी इकट्ठा करता है, और इसे अनुरोधित कंटेनर पर हाथ देता है।

वॉटरिकेटर एक राज्यक्षक आवंटक है, और हमारे उदाहरण के निर्माण पर हम दो मोड चुन सकते हैं;

  1. काम एरिक, जो पानी के नीचे को हासिल करेगा ताजा पानी वसंत में, जबकि यह भी उपायों (और रिपोर्ट) जल स्तर और पवित्रता।

  2. एडम, जो पिछवाड़े में टैप आउट का उपयोग करता है और लॉगिंग के बारे में कुछ भी परवाह नहीं करता है। एडमएरिक से बहुत तेज़ है।


कोई फर्क नहीं पड़ता है जहाँ पानी हम हमेशा से आता है उसी तरह से इसके बारे में निपटाने; हमारे पौधों को पानी से। यहां तक ​​कि अगर हमारे पास एक उदाहरण है जहां एरिक हमें पानी (मेमोरी) की आपूर्ति कर रहा है, और दूसरा जहां Adam टैप का उपयोग कर रहा है, वॉटरिकेटरoperator== के बराबर तुलना करें।

किसी द्वारा किए गए आवंटन को दूसरे द्वारा हटाया जा सकता है।


ऊपर एक मूर्खतापूर्ण similie हो सकता है, लेकिन कल्पना हम एक संभाजक जो हर आवंटन पर लॉग इन करने के करता है, और हम हमारे कोड हमें दिलचस्पी है कि कहीं एक कंटेनर पर इस का उपयोग करता है; हम बाद में तत्वों को इस कंटेनर से दूसरे में ले जाना चाहते हैं .. लेकिन हम अब लॉगिंग में रुचि नहीं रखते हैं।

स्टेटफुल allocators, और propagate_on_container_* बंद करने का विकल्प के बिना, हम करने के लिए मजबूर किया जाएगा या तो 1) कॉपी हर तत्व शामिल 2) कि (अब आवश्यक) प्रवेश के साथ अटक सकता।

0

यह इतना है कि स्टैंडर्ड propagate_on_container_swap करने के लिए कारण अपरिभाषित व्यवहार की अनुमति देता है नहीं है, लेकिन स्टैंडर्ड यह मान के माध्यम से अपरिभाषित व्यवहार को उजागर करता है कि!


एक साधारण उदाहरण एक दायरे वाला संभाजक है, जो एक स्थानीय पूल से स्मृति आवंटित करता है पर विचार करना है, और जो ने कहा कि जब संभाजक दायरे से बाहर चला जाता है पूल हटा दी जाती है:

template <typename T> 
class scoped_allocator; 

अब, हम करते हैं इसका उपयोग करें:

int main() { 
    using scoped = scoped_allocator<int>; 

    scoped outer_alloc; 
    std::vector<int, scoped> outer{outer_alloc}; 

    outer.push_back(3); 

    { 
     scoped inner_alloc; 
     std::vector<int, scoped> inner{inner_alloc}; 

     inner.push_back(5); 

     swap(outer, inner); // Undefined Behavior: loading... 
    } 

    // inner_allocator is dead, but "outer" refers to its memory 
} 
संबंधित मुद्दे