2012-07-28 19 views
5

मैं एक कंटेनर लिख रहा हूँ और कस्टम allocators उपयोग करने के लिए उपयोगकर्ता की अनुमति के लिए चाहते हैं, लेकिन मैं नहीं बता सकता कि क्या मैं संदर्भ द्वारा या मूल्य से चारों ओर allocators पास करना चाहिए।क्या मुझे लगता है कि आवंटकों को अपना मेमोरी पूल सीधे नहीं पकड़ता है (और इसलिए कॉपी किया जा सकता है)?

यह गारंटी है (या कम से कम, एक उचित धारणा बनाने के लिए) है कि एक संभाजक वस्तु नहीं अपनी स्मृति पूल सीधे शामिल होंगे, और इसलिए यह एक संभाजक कॉपी और allocators की स्मृति पूल की उम्मीद करने के लिए ठीक हो जाएगा पार संगत होने के लिए? या क्या मुझे हमेशा संदर्भ के अनुसार आवंटकों को पास करने की आवश्यकता है?

(मैं ने पाया है कि> 2 का एक पहलू से संदर्भ नुकसान प्रदर्शन से गुजर रहा है क्योंकि संकलक अलियासिंग के बारे में चिंता शुरू होता है, तो यह एक या नहीं, मैं इस धारणा पर भरोसा कर सकते बना देता है।)

+0

क्या आप कृपया जो कुछ करने का प्रयास कर रहे हैं उसके कुछ विवरण जोड़ सकते हैं? आपको स्पष्ट रूप से राज्य के आवंटकों से सावधान रहना होगा, लेकिन कुछ मानक संचालन जैसे कि एक कंटेनर को दूसरे से स्थानांतरित करना मानक मुहावरे हैं। –

+0

@KerrekSB: आह, ठीक है। उदाहरण के लिए, मैं "प्रतिलिपि 'या' आरक्षित 'ऑपरेशन ऑपरेशन को" कॉपी-इन-कंटेनर-एंड-स्वैप "ऑपरेशन के रूप में कार्यान्वित करने की कोशिश कर रहा हूं। यह काम करता है, अगर आवंटक के पास अपना स्वयं का मेमोरी पूल नहीं है। (यदि ऐसा होता है, तो मैं दो बार स्वैप कर सकता हूं, लेकिन एक प्रतिलिपि बनाना अभी भी महंगा हो सकता है और यदि इसमें अपना पूल होता है तो स्टैक ओवरफ़्लो हो सकता है।) – Mehrdad

उत्तर

9

सी ++ 11 खंड 17.6.3.5 आवंटन आवश्यकताओं [allocator.requirements] आवंटकों को अनुरूप बनाने के लिए आवश्यकताओं को निर्दिष्ट करता है। आवश्यकताओं में से हैं:

X     an Allocator class for type T 
... 
a, a1, a2   values of type X& 
... 
a1 == a2    bool   returns true only if storage 
            allocated from each can be 
            deallocated via the other. 
            operator== shall be reflexive, 
            symmetric, and transitive, and 
            shall not exit via an exception. 
... 
X a1(a);       Shall not exit via an exception. 
            post: a1 == a 

आईई। जब आप एक आवंटक की प्रतिलिपि बनाते हैं, तो दो प्रतियों को एक-दूसरे के पॉइंटर्स को हटाने में सक्षम होना आवश्यक होता है।

निश्चित रूप से कोई भी आंतरिक बफर आवंटकों में डाल सकता है, लेकिन प्रतियों को अन्य बफर की सूची रखना होगा। या शायद एक आवंटक के पास एक आविष्कार हो सकता है कि डीलोकेशन हमेशा एक नो-ऑप होता है क्योंकि सूचक हमेशा आंतरिक बफर से आता है (या तो स्वयं से, या किसी अन्य प्रतिलिपि से)।

लेकिन जो कुछ भी योजना है, प्रतियां "पार-संगत" होना चाहिए।

अद्यतन

यहाँ एक सी ++ 11 अनुरूप संभाजक कि "छोटे स्ट्रिंग अनुकूलन" करता है।

#include <cstddef> 

template <std::size_t N> 
class arena 
{ 
    static const std::size_t alignment = 16; 
    alignas(alignment) char buf_[N]; 
    char* ptr_; 

    std::size_t 
    align_up(std::size_t n) {return n + (alignment-1) & ~(alignment-1);} 

public: 
    arena() : ptr_(buf_) {} 
    arena(const arena&) = delete; 
    arena& operator=(const arena&) = delete; 

    char* allocate(std::size_t n) 
    { 
     n = align_up(n); 
     if (buf_ + N - ptr_ >= n) 
     { 
      char* r = ptr_; 
      ptr_ += n; 
      return r; 
     } 
     return static_cast<char*>(::operator new(n)); 
    } 
    void deallocate(char* p, std::size_t n) 
    { 
     n = align_up(n); 
     if (buf_ <= p && p < buf_ + N) 
     { 
      if (p + n == ptr_) 
       ptr_ = p; 
     } 
     else 
      ::operator delete(p); 
    } 
}; 

template <class T, std::size_t N> 
class stack_allocator 
{ 
    arena<N>& a_; 
public: 
    typedef T value_type; 

public: 
    template <class U> struct rebind {typedef stack_allocator<U, N> other;}; 

    explicit stack_allocator(arena<N>& a) : a_(a) {} 
    template <class U> 
     stack_allocator(const stack_allocator<U, N>& a) 
      : a_(a.a_) {} 
    stack_allocator(const stack_allocator&) = default; 
    stack_allocator& operator=(const stack_allocator&) = delete; 

    T* allocate(std::size_t n) 
    { 
     return reinterpret_cast<T*>(a_.allocate(n*sizeof(T))); 
    } 
    void deallocate(T* p, std::size_t n) 
    { 
     a_.deallocate(reinterpret_cast<char*>(p), n*sizeof(T)); 
    } 

    template <class T1, std::size_t N1, class U, std::size_t M> 
    friend 
    bool 
    operator==(const stack_allocator<T1, N1>& x, const stack_allocator<U, M>& y); 

    template <class U, std::size_t M> friend class stack_allocator; 
}; 

template <class T, std::size_t N, class U, std::size_t M> 
bool 
operator==(const stack_allocator<T, N>& x, const stack_allocator<U, M>& y) 
{ 
    return N == M && &x.a_ == &y.a_; 
} 

template <class T, std::size_t N, class U, std::size_t M> 
bool 
operator!=(const stack_allocator<T, N>& x, const stack_allocator<U, M>& y) 
{ 
    return !(x == y); 
} 

यह इस तरह इस्तेमाल किया जा सकता: यह सी ++ 11 अनुरूप बनाने के लिए, मैं डाल करने के लिए "आंतरिक" संभाजक के लिए बाहरी बफ़र ताकि प्रतियां बराबर हैं पड़ा

#include <vector> 

template <class T, std::size_t N> using A = stack_allocator<T, N>; 
template <class T, std::size_t N> using Vector = std::vector<T, stack_allocator<T, N>>; 

int main() 
{ 
    const std::size_t N = 1024; 
    arena<N> a; 
    Vector<int, N> v{A<int, N>(a)}; 
    v.reserve(100); 
    for (int i = 0; i < 100; ++i) 
     v.push_back(i); 
    Vector<int, N> v2 = std::move(v); 
    v = v2; 
} 

सभी उपरोक्त समस्या के लिए आवंटन स्थानीय arena से खींचे गए हैं जो आकार में 1 Kb है। आप इस आवंटक को मूल्य या संदर्भ के आधार पर पास करने में सक्षम होना चाहिए।

+0

+1 के लिए धन्यवाद यह ठीक जवाब! –

+0

+1 वास्तव में, जो मेरे प्रश्न का उत्तर सीधे देता है। (और एसओ पर आपके अन्य उत्तर भी बहुत उपयोगी हैं, बीटीडब्ल्यू।) धन्यवाद एक गुच्छा! :) – Mehrdad

+0

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

11

वर्ष सी ++ इन आवश्यकताओं को शामिल है कि यदि आप Alloc<T> a, b, तो a == b, है और आप b उपयोग कर सकते हैं चीजें हैं जो a साथ आबंटित किया गया पुनःआवंटन रहे हैं: मानक एक मानक अनुरूप संभाजक के लिए आवश्यकताओं को आसान बनाता है। आवंटक मूल रूप से स्टेटलेस हैं।


सी ++ 11 में, स्थिति एक बहुत अधिक शामिल हो गया है, वहाँ के रूप में अब स्टेटफुल allocators के लिए समर्थन करते हैं। जैसे-जैसे आप प्रतिलिपि बनाते हैं और वस्तुओं को स्थानांतरित करते हैं, वहां विशिष्ट नियम हैं कि क्या एक कंटेनर की प्रतिलिपि बनाई जा सकती है या अन्य कंटेनर से स्थानांतरित किया जा सकता है यदि आवंटक भिन्न होते हैं, और आवंटकों को प्रतिलिपि या स्थानांतरित कैसे किया जाता है।

बस पहले अपना सवाल का जवाब देने: नहीं, आप निश्चित रूप से नहीं मान सकते हैं कि यह समझ में आता है चारों ओर अपने संभाजक कॉपी करने के लिए, और अपने संभाजक भी copyable नहीं हो सकता।

यहाँ इस विषय पर 23.2.1/7:

जब तक अन्यथा उल्लिखित, सभी कंटेनर इस खंड में परिभाषित स्मृति प्राप्त एक संभाजक (17.6.3.5 देखें) का उपयोग। इन कंटेनर प्रकारों के लिए कन्स्ट्रक्टर कॉपी करें अपने पहले पैरामीटर पर allocator_traits<allocator_-type>::select_on_container_copy_construction पर कॉल करके आवंटक प्राप्त करें। कंटेनर को स्थानांतरित करने वाले कंटेनर से जुड़े आवंटक से निर्माण को स्थानांतरित करके एक आवंटक प्राप्त करें। आवंटन के इस तरह के कदम निर्माण अपवाद के माध्यम से बाहर नहीं निकलेगा। इन कंटेनर प्रकार के लिए अन्य सभी निर्माताओं के एक Allocator& तर्क (17.6.3.5), एक संभाजक जिसका मान प्रकार कंटेनर की मान प्रकार के समान ही है ले लो। [नोट: यदि कोई कन्स्ट्रक्टर का आमंत्रण वैकल्पिक आवंटक तर्क के डिफ़ॉल्ट मान का उपयोग करता है, तो आवंटक प्रकार को मान प्रारंभिकरण का समर्थन करना चाहिए। -जेंड नोट] इस आवंटक की एक प्रति का उपयोग इन कंटेनरों द्वारा और सभी सदस्य कार्यों द्वारा, प्रत्येक कंटेनर ऑब्जेक्ट के जीवनकाल के दौरान या आवंटक को प्रतिस्थापित करने तक, किसी भी स्मृति आवंटन के लिए किया जाता है। आवंटक केवल असाइनमेंट या स्वैप() के माध्यम से बदला जा सकता है। संभाजक प्रतिस्थापन प्रति असाइनमेंट, चाल काम, या संभाजक केवल तभी allocator_traits<allocator_type>::propagate_on_container_copy_assignment::value की अदला-बदली से, allocator_traits<allocator_type>::propagate_on_container_move_assignment::value, या किया जाता है allocator_traits<allocator_type>::propagate_on_container_swap::value इसी कंटेनर आपरेशन के कार्यान्वयन के भीतर सच है। जब तक वस्तुओं की अदला-बदली की जा रही allocators कि बराबर की तुलना या allocator_traits<allocator_type>::propagate_on_container_swap::value सच है एक कंटेनर की अदला-बदली फ़ंक्शन की कॉल के व्यवहार अनिर्धारित रहता है। सभी कंटेनर प्रकार इस खंड में परिभाषित में, सदस्य get_allocator() कि अगर संभाजक प्रतिस्थापित किया गया है, सबसे हाल ही में प्रतिस्थापन की एक प्रति कंटेनर का निर्माण करने या इस्तेमाल किया संभाजक की एक प्रति देता है।

सारांश के लिए std::allocator_traits के दस्तावेज़ भी देखें।

+0

ओह्ह्ह्ह्ह्ह्ह ... इसलिए एक ['scoped_allocator_adaptor'] है (http: // en विशेष रूप से इस उद्देश्य के लिए .cppreference.com/w/cpp/memory/scoped_allocator_adaptor)? मुझे लगता है कि इसका मतलब है कि मैं सिर्फ उन्हें अंधाधुंध कॉपी नहीं कर सकता। :(+1 भयानक उत्तर, धन्यवाद! – Mehrdad

+0

@ मेहरदैड: स्कॉप्ड चीज उन परिस्थितियों के लिए है जहां आपके कंटेनर के कंटेनर हैं और आप चाहते हैं कि आपका कस्टम आवंटक हर जगह नीचे इस्तेमाल किया जाए। –

+0

ओह हम्म दिलचस्प। – Mehrdad