क्या यह एक ही वेक्टर से एक तत्व push_back के लिए सुरक्षित है?


126
vector<int> v;
v.push_back(1);
v.push_back(v[0]);

यदि दूसरा पुश_बैक एक वास्तविक स्थिति का कारण बनता है, तो वेक्टर में पहले पूर्णांक का संदर्भ मान्य नहीं होगा। तो यह सुरक्षित नहीं है?

vector<int> v;
v.push_back(1);
v.reserve(v.size() + 1);
v.push_back(v[0]);

यह इसे सुरक्षित बनाता है?


4
एक नोट: वर्तमान में मानक प्रस्ताव फोरम में चर्चा चल रही है। इसके एक भाग के रूप में, किसी ने इसका एक उदाहरणpush_back दिया । एक अन्य पोस्टर में एक बग का उल्लेख किया गया था , कि यह आपके द्वारा वर्णित मामले को ठीक से संभाल नहीं पाया। कोई और नहीं, जहां तक ​​मैं बता सकता हूं, तर्क दिया कि यह एक बग नहीं था। यह कहते हुए कि यह निर्णायक सबूत नहीं है, सिर्फ एक अवलोकन है।
बेंजामिन लिंडले

9
मुझे क्षमा करें, लेकिन मुझे नहीं पता कि कौन सा उत्तर स्वीकार करना है क्योंकि सही उत्तर पर अभी भी विवाद है।
नील कर्क

4
मुझे 5 वें टिप्पणी के तहत इस सवाल पर टिप्पणी करने के लिए कहा गया था: stackoverflow.com/a/18647445/576911 । मैं ऐसा कर रहा हूं जो वर्तमान में कहता है कि हर उत्तर को अपवोट कर रहा है: हाँ, यह उसी वेक्टर से एक तत्व push_back करने के लिए सुरक्षित है।
हावर्ड हिनांत

2
@BenVoigt: <shrug> अगर आप मानक के अनुसार सहमत नहीं हैं, या भले ही आप मानक से सहमत हों, लेकिन यह मत कहो कि यह स्पष्ट रूप से पर्याप्त है, तो यह हमेशा आपके लिए एक विकल्प है: cplusplus.github.io/LWG/ lwg-active.html # submit_issue मैंने यह विकल्प खुद से अधिक बार लिया है जितना मैं याद रख सकता हूं। कभी-कभी सफलतापूर्वक, कभी-कभी नहीं। यदि आप बहस करना चाहते हैं कि मानक क्या कहता है, या उसे क्या कहना चाहिए, तो एसओ एक प्रभावी मंच नहीं है। हमारी बातचीत का कोई अर्थ नहीं है। लेकिन आप ऊपर दिए गए लिंक का पालन करके एक आदर्श प्रभाव पर एक मौका दे सकते हैं।
हावर्ड हिनान्ट

2
@ Polaris878 यदि पुश_बैक वेक्टर को उसकी क्षमता तक पहुंचने का कारण बनता है, तो वेक्टर एक नया बड़ा बफर आवंटित करेगा, पुराने डेटा पर कॉपी करेगा, और फिर पुराने बफर को हटा देगा। फिर वह नया तत्व डालेगा। समस्या यह है, नया तत्व पुराने बफर में डेटा का एक संदर्भ है जिसे अभी हटा दिया गया है। जब तक push_back हटाने से पहले मूल्य की एक प्रतिलिपि नहीं बनाता है, यह एक बुरा संदर्भ होगा।
नील कर्क

जवाबों:


31

ऐसा लगता है कि http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html#526 ने इस समस्या को संबोधित किया (या इसके समान कुछ) मानक में एक संभावित दोष के रूप में:

1) समारोह के निष्पादन के दौरान कॉन्स्ट रेफरेंस द्वारा लिए गए पैरामीटर्स बदले जा सकते हैं

उदाहरण:

दिए गए std :: वेक्टर v:

v.insert (v.begin (), v [2]);

v [2] वेक्टर के चलते तत्वों द्वारा बदला जा सकता है

प्रस्तावित प्रस्ताव यह था कि यह एक दोष नहीं था:

वेक्टर :: सम्मिलित (iter, value) कार्य करने के लिए आवश्यक है क्योंकि मानक इसके लिए काम नहीं करने की अनुमति नहीं देता है।


मुझे 17.6.4.9 में अनुमति मिलती है: "यदि किसी फ़ंक्शन के तर्क में अमान्य मान है (जैसे फ़ंक्शन के डोमेन के बाहर का मान या इसके इच्छित उपयोग के लिए कोई अमान्य अमान्य), तो व्यवहार अपरिभाषित है।" यदि रियलाइजेशन होता है, तो सभी पुनरावृत्तियों और तत्वों के संदर्भ अमान्य हैं, जिसका अर्थ है कि फ़ंक्शन को दिया गया पैरामीटर संदर्भ भी अमान्य है।
बेन वोइगट

4
मुझे लगता है कि यह मुद्दा यह है कि कार्यान्वयन रियललोकेशन करने के लिए जिम्मेदार है। यदि इनपुट को शुरू में परिभाषित किया गया है, तो व्यवहार को परिभाषित करने के लिए यह उस पर निर्भर है। चूंकि चश्मा स्पष्ट रूप से निर्दिष्ट करते हैं कि पुश_बैक एक प्रतिलिपि बनाता है, कार्यान्वयन समय की कीमत पर, कार्यान्वयन, शायद, कैश या डी-आवंटन से पहले सभी मूल्यों की प्रतिलिपि बनाना चाहिए। चूँकि इस विशेष प्रश्न में कोई बाहरी संदर्भ नहीं बचा है, इसलिए कोई फर्क नहीं पड़ता कि पुनरावृत्तियों और संदर्भों को अमान्य कर दिया गया है।
ओलिविएड

3
@NeilKirk मुझे लगता है कि यह आधिकारिक जवाब होना चाहिए, यह भी Reddit पर Stephan T. Lavavej ने अनिवार्य रूप से समान तर्कों का उपयोग करके उल्लेख किया है।
टेम्प्लेटेक्स

v.insert(v.begin(), v[2]);एक वास्तविक स्थान को ट्रिगर नहीं कर सकता। तो इस सवाल का जवाब कैसे देता है?
थॉमसमैक्लोड

@ThomasMcLeod: हाँ, यह स्पष्ट रूप से एक वास्तविककरण को ट्रिगर कर सकता है। आप एक नए तत्व को सम्मिलित करके वेक्टर के आकार का विस्तार कर रहे हैं।
वायलेट जिराफ

21

हां, यह सुरक्षित है, और मानक पुस्तकालय कार्यान्वयन इसे बनाने के लिए हुप्स के माध्यम से कूदते हैं।

मेरा मानना ​​है कि कार्यान्वयनकर्ता इस आवश्यकता को २३.२ / ११ में किसी भी तरह ट्रेस कर लेते हैं, लेकिन मैं यह पता नहीं लगा सकता कि कैसे, और मुझे कुछ और ठोस भी नहीं मिल रहा है। सबसे अच्छा मैं यह लेख पा सकता हूं:

http://www.drdobbs.com/cpp/copying-container-elements-from-the-c-li/240155771

Libc ++ के और libstdc ++ के कार्यान्वयन के निरीक्षण से पता चलता है कि वे भी सुरक्षित हैं।


9
कुछ समर्थन वास्तव में यहाँ मदद करेगा।
क्रिस

3
यह दिलचस्प है, मुझे मानना ​​होगा कि मैंने कभी इस मामले पर विचार नहीं किया था, लेकिन वास्तव में इसे हासिल करना काफी मुश्किल है। क्या यह भी है vec.insert(vec.end(), vec.begin(), vec.end());?
मैथ्यू एम।

2
@MatthieuM। नहीं: तालिका 100 कहती है: "पूर्व: i और j पुनरावृत्त नहीं हैं"।
सेबेस्टियन रेडल सेप

2
मैं अब उत्थान कर रहा हूं क्योंकि यह मेरी याद के रूप में अच्छी तरह से है, लेकिन एक संदर्भ की आवश्यकता है।
bames53

3
क्या आप जिस संस्करण का उपयोग कर रहे हैं उसमें २३.२ / ११ है "जब तक अन्यथा विशिष्ट fi एड (या तो स्पष्ट रूप से या अन्य कार्यों के संदर्भ में किसी फ़ंक्शन को परिभाषित करना), कंटेनर सदस्य फ़ंक्शन को आमंत्रित करना या किसी लाइब्रेरी फ़ंक्शन के तर्क के रूप में कंटेनर पास करना पुनरावृत्तियों को अमान्य नहीं करेगा , या उस कंटेनर में मौजूद वस्तुओं के मूल्यों को बदल सकते हैं। " ? लेकिन vector.push_backयह अन्यथा निर्दिष्ट करता है। "यदि वास्तविक आकार पुरानी क्षमता से अधिक है तो पुन: प्राप्ति का कारण बनता है।" और (पर reserve) "Reallocation अनुक्रम में तत्वों का संदर्भ देते हुए सभी संदर्भों, बिंदुओं और पुनरावृत्तियों को अमान्य करता है।"
बेन वोइग्ट

13

मानक सुरक्षित होने के लिए आपके पहले उदाहरण की भी गारंटी देता है। कोटेशन C ++ 11

[Sequence.reqmts]

3 टेबल्स 100 और 101 में ... Xएक अनुक्रम कंटेनर वर्ग को aदर्शाता है , Xप्रकार के तत्वों से युक्त मूल्य को दर्शाता है T, ... tएक लैवल्यू या एक गतिरोध का संकेत देता हैX::value_type

16 टेबल 101 ...

अभिव्यक्ति a.push_back(t) वापसी प्रकार void आपरेशनल अर्थ विज्ञान की एक प्रति जोड़ता t. आवश्यक है: T किया जाएगा CopyInsertableमें Xकंटेनर basic_string , deque, list,vector

तो भले ही यह बिल्कुल तुच्छ नहीं है, लेकिन कार्यान्वयन की गारंटी होगी कि इसे करते समय संदर्भ को अमान्य नहीं किया जाएगा push_back


7
मैं यह नहीं देखता कि यह कैसे सुरक्षित होने की गारंटी देता है।
जर्क

4
@Angew: यह पूरी तरह से अमान्य है t, एकमात्र सवाल यह है कि क्या कॉपी बनाने से पहले या बाद में। आपका अंतिम वाक्य निश्चित रूप से गलत है।
बेन वोइगट सेप

4
@BenVoigt tसूचीबद्ध व्यवहारों को पूरा करने के बाद से वर्णित व्यवहार की गारंटी है। किसी पूर्व शर्त को अमान्य करने के लिए कार्यान्वयन की अनुमति नहीं है और फिर इसका उपयोग किसी बहाने के रूप में निर्दिष्ट व्यवहार नहीं करने के लिए किया जाता है।
bames53

8
@BenVoigt ग्राहक कॉल के दौरान पूर्व शर्त बनाए रखने के लिए बाध्य नहीं है; केवल यह सुनिश्चित करने के लिए कि यह कॉल की दीक्षा में मिला है।
bames53

6
@BenVoigt यह एक अच्छा बिंदु है, लेकिन मेरा मानना ​​है कि इसमें पारित फ़ंक्शंस को पुनरावृत्तियों for_eachको अमान्य नहीं करने के लिए आवश्यक है। मैं किसी संदर्भ के लिए नहीं आ सकता for_each, लेकिन मैं "ऑप और बाइनरी_ऑप को पुनरावृत्तियों या सबग्रेजों को अमान्य नहीं करूंगा" जैसे कुछ एल्गोरिदम टेक्स्ट पर देखता हूं।
bames53

7

यह स्पष्ट नहीं है कि पहला उदाहरण सुरक्षित है, क्योंकि सबसे सरल कार्यान्वयन push_backपहले वेक्टर को फिर से विभाजित करना होगा, यदि आवश्यक हो, और फिर संदर्भ की प्रतिलिपि बनाएँ।

लेकिन कम से कम यह विज़ुअल स्टूडियो 2010 के साथ सुरक्षित प्रतीत होता है। इसका कार्यान्वयन push_backउस मामले की विशेष हैंडलिंग करता है जब आप वेक्टर में एक तत्व को पीछे धकेलते हैं। कोड इस प्रकार संरचित है:

void push_back(const _Ty& _Val)
    {   // insert element at end
    if (_Inside(_STD addressof(_Val)))
        {   // push back an element
                    ...
        }
    else
        {   // push back a non-element
                    ...
        }
    }

8
मैं जानना चाहूंगा कि क्या विनिर्देश के लिए यह सुरक्षित होना चाहिए।
नवाज

1
मानक के अनुसार इसे सुरक्षित होने की आवश्यकता नहीं है। हालांकि, इसे सुरक्षित तरीके से लागू करना संभव है।
बेन वोइगट

2
@BenVoigt मैं कहना चाहता हूँ यह है सुरक्षित होने के लिए आवश्यक (मेरा उत्तर देखें)।
एंग्यू को अब

2
@BenVoigt जब आप संदर्भ पास करते हैं, तो यह मान्य है।
एंग्यू को अब SO

4
@Angew: यह पर्याप्त नहीं है। आपको एक संदर्भ पास करना होगा जो कॉल की अवधि के लिए वैध रहता है, और यह एक नहीं है।
बेन वोइग्ट सेप

3

यह मानक से कोई गारंटी नहीं है, लेकिन एक अन्य डेटा बिंदु के रूप में, LLVM के libc ++ केv.push_back(v[0]) लिए सुरक्षित है ।

libc ++ कीstd::vector::push_back कॉल __push_back_slow_pathतब होती है जब उसे मेमोरी को पुनः लोड करने की आवश्यकता होती है:

void __push_back_slow_path(_Up& __x) {
  allocator_type& __a = this->__alloc();
  __split_buffer<value_type, allocator_type&> __v(__recommend(size() + 1), 
                                                  size(), 
                                                  __a);
  // Note that we construct a copy of __x before deallocating
  // the existing storage or moving existing elements.
  __alloc_traits::construct(__a, 
                            _VSTD::__to_raw_pointer(__v.__end_), 
                            _VSTD::forward<_Up>(__x));
  __v.__end_++;
  // Moving existing elements happens here:
  __swap_out_circular_buffer(__v);
  // When __v goes out of scope, __x will be invalid.
}

मौजूदा भंडारण से निपटने से पहले न केवल प्रतिलिपि बनाई जानी चाहिए, बल्कि मौजूदा तत्वों से आगे बढ़ने से पहले। मुझे लगता है कि मौजूदा तत्वों की चलती है __swap_out_circular_buffer, जिस स्थिति में यह कार्यान्वयन वास्तव में सुरक्षित है।
बेन वोइग्ट सेप

@BenVoigt: अच्छी बात है, और आप वास्तव में सही हैं कि चलती अंदर होती है __swap_out_circular_buffer। (मैंने नोट करने के लिए कुछ टिप्पणियां जोड़ी हैं।)
नैट कोहल सेप

1

पहला संस्करण निश्चित रूप से सुरक्षित नहीं है:

मानक पुस्तकालय कंटेनर या स्ट्रिंग सदस्य फ़ंक्शन को कॉल करके प्राप्त किए गए पुनरावृत्तियों पर संचालन अंतर्निहित कंटेनर तक पहुंच सकता है, लेकिन इसे संशोधित नहीं करेगा। [नोट: विशेष रूप से, कंटेनर संचालन जो उस कंटेनर से जुड़े पुनरावृत्तियों पर संचालन के साथ पुनरावृत्तियों को अमान्य करते हैं। - अंतिम नोट]

धारा 17.6.5.9 से


ध्यान दें कि यह डेटा दौड़, जो लोगों को सामान्य रूप से सूत्रण के साथ संयोजन के रूप में के बारे में सोच पर अनुभाग है ... लेकिन वास्तविक परिभाषा शामिल रिश्ते "से पहले होता है", और मैं के कई दुष्प्रभाव के बीच किसी भी आदेश देने के संबंध नहीं दिख रहा है push_backमें यहाँ खेलते हैं, अर्थात् संदर्भ अमान्य होना नए टेल एलिमेंट की कॉपी-कंस्ट्रक्शन के संबंध में आदेश के रूप में परिभाषित नहीं किया गया है।


1
यह समझा जाना चाहिए कि यह एक नोट है, एक नियम नहीं है, इसलिए यह पूर्वगामी नियम का एक परिणाम बता रहा है ... और परिणाम संदर्भों के लिए समान हैं।
बेन वोइग्ट सेप

5
के परिणाम का v[0]एक पुनरावृत्ति नहीं है, इसी तरह, push_back()एक पुनरावृत्ति नहीं लेता है। तो, एक भाषा वकील के दृष्टिकोण से, आपका तर्क शून्य है। माफ़ करना। मुझे पता है, कि अधिकांश पुनरावृत्तियां संकेत हैं, और एक पुनरावृत्ति को अमान्य करने का बिंदु, संदर्भ के लिए बहुत समान है, लेकिन मानक का वह हिस्सा जिसे आप उद्धृत करते हैं, हाथ में स्थिति के लिए अप्रासंगिक है।
cmaster

-1। यह पूरी तरह से अप्रासंगिक उद्धरण है और वैसे भी इसका जवाब नहीं देता है। समिति का कहना x.push_back(x[0])है कि सेफ है।
नवाज

0

यह पूरी तरह से सुरक्षित है।

आपके दूसरे उदाहरण में

v.reserve(v.size() + 1);

जिसकी आवश्यकता नहीं है क्योंकि यदि वेक्टर अपने आकार से बाहर चला जाता है, तो यह छोटा होगा reserve

वेक्टर इस सामान के लिए जिम्मेदार है, न कि आप।


-1

दोनों सुरक्षित हैं क्योंकि push_back मान को कॉपी करेगा, न कि संदर्भ को। यदि आप पॉइंटर्स स्टोर कर रहे हैं, तो वह अभी भी सुरक्षित है जहां तक ​​वेक्टर का संबंध है, लेकिन बस यह जान लें कि आपके वेक्टर के दो तत्व एक ही डेटा को इंगित करेंगे।

धारा 23.2.1 सामान्य कंटेनर आवश्यकताएँ

16
  • a.push_back (t) t की एक प्रति लागू करता है। की आवश्यकता है: टी X में CopyInsertable होना चाहिए।
  • a.push_back (rv) rv की एक प्रति भेजता है। आवश्यकता है: टी एक्स में मूवमेंटेबल होगा।

इसलिए push_back के कार्यान्वयन को यह सुनिश्चित करना चाहिए कि एक प्रति v[0] डाली गई है। उदाहरण के तौर पर, एक ऐसा क्रियान्वयन मानकर जो नकल करने से पहले फिर से हो जाएगा, यह जानबूझकर नकल की नकल नहीं करेगा v[0]और इस तरह के चश्मे का उल्लंघन करेगा।


2
push_backहालांकि , वेक्टर का आकार भी बदल जाएगा , और एक भोले-भाले क्रियान्वयन में यह कॉपी होने से पहले संदर्भ को अमान्य कर देगा । इसलिए जब तक आप मानक से प्रशस्ति पत्र द्वारा इसे वापस नहीं ले सकते, मैं इसे गलत मानूंगा।
कोनराड रुडोल्फ

4
"यह" से, क्या आप पहले या दूसरे उदाहरण से मतलब रखते हैं? push_backवेक्टर में मूल्य की नकल करेगा; लेकिन (जहाँ तक मैं देख सकता हूँ) जो कि वास्तविककरण के बाद हो सकता है , जिस बिंदु पर यह प्रतिलिपि बनाने की कोशिश कर रहा है वह संदर्भ अब मान्य नहीं है।
माइक सेमोर

1
push_backसंदर्भ द्वारा अपना तर्क प्राप्त करता है ।
bames53

1
@ ऑलिवर: इसे (1) नए स्थान को आवंटित करना होगा (2) नए तत्व की प्रतिलिपि बनाएँ (3) मौजूदा तत्वों को स्थानांतरित करना (4) स्थानांतरित तत्वों से नष्ट करना (5) पुराने भंडारण को मुक्त करना - THAT क्रम में - पहले संस्करण का काम करने के लिए।
बेन Voigt

1
@BenVoigt किसी अन्य कंटेनर को आवश्यकता क्यों होगी कि एक प्रकार की प्रतिलिपि हो सकती है यदि वह पूरी तरह से उस संपत्ति को अनदेखा करने जा रही है?
15

-2

23.3.6.5/1 से: Causes reallocation if the new size is greater than the old capacity. If no reallocation happens, all the iterators and references before the insertion point remain valid.

चूंकि हम अंत में सम्मिलित कर रहे हैं, यदि वेक्टर का आकार परिवर्तन नहीं किया गया है तो कोई संदर्भ अमान्य नहीं होगा । तो अगर वेक्टर का capacity() > size()काम करने की गारंटी है, तो यह अपरिभाषित व्यवहार की गारंटी है।


मेरा मानना ​​है कि विनिर्देश वास्तव में या तो मामले में काम करने की गारंटी देता है। मैं हालांकि एक संदर्भ पर इंतजार कर रहा हूं।
bames53

प्रश्न में पुनरावृत्तियों या पुनरावृत्ति सुरक्षा का कोई उल्लेख नहीं है।
ओलिवियरड

3
@ ऑइलियर डी इट्रिअर भाग यहां बहुत अधिक है: मैं referencesबोली के हिस्से में दिलचस्पी रखता हूं ।
मार्क बी

2
यह वास्तव में सुरक्षित होने की गारंटी है (मेरा उत्तर देखें, शब्दार्थ push_back)।
Angew को अब SO
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.