2013-06-12 10 views
5

के बिना std :: वेक्टर के आकार को कम करना मेरे पास टेम्पलेट तर्क के std::vector का उपयोग करके एक टेम्पलेट श्रेणी है। तर्क डिफ़ॉल्ट रचनात्मक नहीं हो सकता है। मैं वेक्टर के आकार को कम करना चाहता हूं (इसे किसी दिए गए आकार में काट दें)। जाहिर हैएक डिफ़ॉल्ट कन्स्ट्रक्टर

vec.resize(reduced_size); 

... काम नहीं करता क्योंकि इसे एक डिफ़ॉल्ट कन्स्ट्रक्टर की आवश्यकता होती है।

मैं कर सकता निश्चित रूप से:

  1. किसी भी इस्तेमाल किया प्रकार के लिए डिफ़ॉल्ट निर्माता बनाने के लिए (जो मुझे इसे जोड़ने के लिए की आवश्यकता है जब यह एक अच्छा डिजाइन विकल्प नहीं हो सकता है)
  2. कार्य करने के लिए एक डिफ़ॉल्ट मान पारित (अंतरफलक के बेकार अव्यवस्था)
  3. जबकि प्रश्न लिखने एक निर्माण विधि टेम्पलेट के लिए (भी बेकार अव्यवस्था)

गुजरती हैं, मैंने देखा है कि मैं यह कर सकते हैं,210 अंत करने के लिए वेक्टर ऊपर से तत्वों:

vec.erase (vec.begin() + position, vec.end()); 

... हालांकि, मैं नहीं यकीन है कि अगर इस resize के रूप में के रूप में कुशल हो जाएगा हूँ।

क्या डिफॉल्ट कन्स्ट्रक्टर के बिना वेक्टर के आकार को कम करने का कोई प्रभावी तरीका है?

सी ++ 11 समाधान स्वीकार्य हैं।


संपादित करें: लगता है कि दोनों MSVC और जीसीसी एक मिटा कॉल के रूप में आकार बदलने के सिकुड़ने को लागू है, इसलिए है कि मेरे प्रदर्शन सवाल का जवाब।

+0

मुझे नहीं पता कि मिटा क्यों कम कुशल होना चाहिए। यह 0 बाइट्स को याद करेगा, और अन्यथा समान होगा। क्या आपने बेंचमार्क किया है? – Dave

+0

http://stackoverflow.com/questions/4820998/resizing-an-stdvector-which-elements-are- प्रभावित –

+0

मैं अभी भी एक सी ++ नौसिखिया हूं, लेकिन मुझे समझ में नहीं आता कि क्यों 'टी' को डिफॉल्ट कन्स्ट्रक्टर की आवश्यकता होगी अगर सिकुड़ते हैं। –

उत्तर

2

मेरी कार्यान्वयन में, ऐसा लगता है कि हमारे पास (कुछ सरलीकरण के साथ):

void std::vector<T,A>::resize(size_type __new_size) 
{ 
    if (__new_size > size()) 
     _M_default_append(__new_size - size()); 
    else if (__new_size < size()) 
     _M_erase_at_end(begin() + __new_size); 
} 

auto std::vector<T,A>::erase(iterator __first, iterator __last) -> iterator 
{ 
    if (__first != __last) 
    { 
     if (__last != end()) 
      _GLIBCXX_MOVE3(__last, end(), __first); 
     _M_erase_at_end(__first + (end() - __last)); 
    } 
    return __first; 
} 

जहां _M_... निजी सदस्य कार्य हैं। आप वास्तव में _M_erase_at_end के प्रभाव चाहते हैं। मुझे लगता है कि _M_default_appendv.resize(sz) से कॉल करने के लिए संकलक के लिए मुश्किल या असंभव होगा, लेकिन v.erase(iter, v.end()) में __last == end() में नोटिस करना और _GLIBCXX_MOVE3 और + (end() - __last) को ऑप्टिमाइज़ करना आसान है। तो erase()resize() से कहीं अधिक कुशल हो सकता है।

मैं अपेक्षा करता हूं कि अधिकांश कार्यान्वयन समान कहानी हों: कुछ सरल if परीक्षण, और फिर अंत में तत्वों के लिए विनाशकों को कॉल करने के लिए कुछ समान विधि को बुलाएं।

+0

साथ प्रयोग करेंगे MSVC में दुर्भाग्य से यह नहीं तो ऐसा करने के लिए लगता है और मैं पोर्टेबल होने की जरूरत:/ –

+0

'erase' तरह से पोर्टेबल है। (आपके प्रश्न से, ऐसा लगता है कि आप पहले से ही महसूस कर चुके हैं।) (इसके अलावा, वास्तव में? उनका 'आकार बदलें') कॉल कलेक्टरों की प्रतिलिपि बनाता है?) – aschepler

+0

संपादित - यह आश्चर्यजनक रूप से यह <आकार के मामले में मिटाने को नहीं कहता है। लेकिन आकार के लिए यह कुछ एमएसवीसी बैल * कहता है जो एक प्रतिलिपि निर्माता की आवश्यकता को तुरंत चालू करता है। –

1

ज़रूर - जब आप resize कहते हैं, आप सही प्रकार है कि (सैद्धांतिक) रिक्त स्थान को भरने के लिए अगर आप resize का उपयोग वेक्टर के आकार को बढ़ाने के लिए इस्तेमाल किया जाएगा के एक मूल्य गुजर एक दूसरा पैरामीटर आपूर्ति कर सकते हैं। सी ++ 03 में, उस तर्क का T() का डिफ़ॉल्ट मान होता है, जहां डिफ़ॉल्ट सीटीआर चीजों में आता है (सी ++ 11 में, वे इसके बजाय ओवरलोडिंग का उपयोग करते हैं, ताकि आप बिना किसी कठिनाई के आकार को कम करने के लिए resize() पर कॉल कर सकें)।

अपने आप के मूल्य को पार करके, आप डिफ़ॉल्ट ctor की आवश्यकता से बचते हैं। जैसा ऊपर बताया गया है, सी ++ 11 में, डिफ़ॉल्ट सीटीआर की आवश्यकता नहीं होगी/भले ही आप दूसरे तर्क को पास न करें।

मुझे संदेह है कि यह erase का उपयोग करने की तुलना में कोई वास्तविक सुधार देगा। विशेष रूप से, मानक में विनिर्देश (§23.3.6.3/9) है: erase(begin() + sz, end()); को

तो sz <= size(), बराबर।

जैसे, इस मामले में resize और erase के बीच कोई अंतर के लिए कोई वास्तविक कारण हो रहा है।

+0

मुझे नहीं लगता कि एक दूसरा तर्क के बिना भी 'आकार बदलें', डिफ़ॉल्ट 'टी' बनाने के किसी भी तरीके से संकलित हो सकता है? निश्चित रूप से, यह अब डिफ़ॉल्ट पैरामीटर के रूप में नहीं हो सकता है, लेकिन जब कंटेनर बड़ा हो जाता है, तो उसे डेटा को कुछ के साथ प्रारंभ करना होगा। और संकलन समय पर, संकलक यह नहीं जानता कि आकार पैरामीटर पहले की तुलना में छोटा है ... – Yakk

+0

@Yakk: हाँ, यदि आप संग्रह के आकार को बढ़ाते हैं, तो यह मान-प्रारंभिक तत्वों को सम्मिलित करता है, लेकिन इसके लिए आवश्यक है केवल CopyInsertable, डिफ़ॉल्ट नहीं है। –

+0

सी ++ 11 में 'आकार बदलने' के लिए कोई दूसरा तर्क नहीं है, आप कहां से आने वाली प्रतिलिपि 'टी' कहां से कॉपी करते हैं? – Yakk

1

erase का उपयोग करने का आपका विचार सही मार्ग है।

template<typename Container> 
Container&& reduce_size(Container&& c, std::size_t amount) { 
    using std::begin; using std::end; // enable ADL 
    amount = std::min(amount, std::size_t(end(c)-begin(c))); // paranoid! 
    c.erase(end(c)-amount, end(c)); 
    return std::forward<Container>(c); // I like my container-algorithms to pass through 
} 

जो के रूप में तेजी से अपने erase कार्यान्वयन के रूप में हो सकता है (अच्छी तरह से, एक और शाखा और जाँच) होगा: भ्रम की मात्रा को कम करने के लिए, मैं एक कंटेनर आधारित एल्गोरिथ्म लिखेंगे।

उपयोग:

std::vector<Foo> blah; 
blah.emplace_back(7); 
reduce_size(blah, 10); 
+0

उपरोक्त। मेरी जरूरतों के लिए मैं शायद एक कटऑफ स्थिति के साथ एक reduce_size से हालांकि राशि :) –

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