2017-09-02 15 views
7

रूप P0532R0 में विस्तार से बताया std::launder अपरिभाषित व्यवहार (यूबी) से बचने के लिए इस्तेमाल किया जाना चाहिए folowing उपयोग के मामले में,:क्या पॉइंटर अंकगणित द्वारा प्रचारित "कपड़े धोने" है?

struct X{ 
    const int i; 
    x(int i):i{i}{} 
    }; 

unsigned char buff[64]; 
auto p = new(buff) X(33); 
p->~X(); 
new(buff) X(42); 
p = std::launder(p); 
assert(p->i==42); 

लेकिन इस मामले में जहां एक से अधिक वस्तु बफर पर है में क्या है (यह ठीक है क्या हो अगर एक एक सदिश में 2 X धक्का, वेक्टर को साफ करता है और उसके बाद दो नए X धक्का) होगा:

unsigned char buff[64]; 
auto p0 = new(buff) X(33); 
auto p1 = new(p0+1) X(34); 
p1->~X(); 
p0->~X(); 
new(buff) X(42); 
new(p0+1) X(43); 
p0 = std::launder(p0); 
assert(p0->i==42); 
assert(p0[1].i==43);//??? 

पिछले दावे सही है, या p0[1] अभी भी यूबी का आह्वान?

+2

'जोर नहीं है (पी 0 [1] == 43); एक अवैध अभिव्यक्ति? ... subexpression 'p0 [1]' द्वारा उत्पादित वर्ग प्रकार को ध्यान में रखते हुए कोई अधिभारित नहीं है 'ऑपरेटर == (एक्स, int) ' – WhiZTiM

+0

@WhiZTiM स्पष्ट टाइपो स्पष्ट है? – Barry

+0

यह वास्तव में एक टाइपो था। – Oliv

उत्तर

5

आपका कोड यूबी को आमंत्रित करता है, लेकिन launder कारणों से नहीं। ऐसा इसलिए है क्योंकि p0[1].i स्वयं यूबी है।

हाँ, वास्तव में ([Expr.Add]/4):

जब एक अभिव्यक्ति अभिन्न प्रकार है कि करने के लिए जोड़ा या एक सूचक से घटाया जाता है, परिणाम सूचक संकार्य के प्रकार है। यदि अभिव्यक्ति पी तत्वों के तत्व x [i] को तत्वों x के साथ इंगित करता है, n तत्वों के साथ x, अभिव्यक्ति पी + जे और जे + पी (जहां जे का मान जे है) इंगित करता है (संभावित-काल्पनिक) तत्व x [i + जे] अगर 0 ≤ मैं + जे ≤ एन; अन्यथा, व्यवहार अपरिभाषित है। इसी तरह, अभिव्यक्ति पी - जे इंगित करता है (संभवतः-काल्पनिक) तत्व x [i - j] अगर 0 ≤ i - j ≤ n; अन्यथा, व्यवहार अपरिभाषित है।

एक ऑब्जेक्ट जो सरणी तत्व नहीं है, इस उद्देश्य के लिए एकल-तत्व सरणी से संबंधित माना जाता है; 8.3.1 देखें। एन तत्वों के सरणी एक्स के अंतिम तत्व से पहले एक पॉइंटर को इस उद्देश्य के लिए एक अनुमानक तत्व x [n] के सूचक के बराबर माना जाता है; 6.9.2 देखें।

[] जब पॉइंटर पर लागू होता है तो पॉइंटर अंकगणित करने का मतलब है। और सी ++ ऑब्जेक्ट मॉडल में, पॉइंटर अंकगणित केवल पॉइंटर्स पर टाइप किए जा रहे प्रकार की सरणी में तत्वों के लिए उपयोग किया जा सकता है। आप हमेशा ऑब्जेक्ट को लम्बाई 1 की सरणी के रूप में देख सकते हैं, ताकि आप एकल ऑब्जेक्ट के "एक अतीत के अंत में" पॉइंटर प्राप्त कर सकें। इस प्रकार, p0 + 1 मान्य है।

मान्य है उस पते पर संग्रहीत ऑब्जेक्ट तक पहुंच रहा है हालांकि पॉइंटर p0 + 1 के माध्यम से प्राप्त किया गया है। यही है, p0[1].i अपरिभाषित व्यवहार है। यह से पहले यूबी जैसा है इसके बाद इसे लॉन्डर करना है।

अब, चलो एक अलग संभावना को देखते हैं:

X x[2]; 
x[1].~X(); //Destroy this object. 
new(x + 1) X; //Construct a new one. 

तो चलो कुछ सवाल पूछना है:

x[1] यूबी है? मैं कहूंगा ... नहीं, यह यूबी नहीं है। क्यूं कर? क्योंकि x[1] नहीं है:

एक सूचक है कि मूल वस्तु की ओर इशारा किया, एक संदर्भ है कि मूल वस्तु के रूप में भेजा, या मूल वस्तु सरणी और पहले के

x अंक की नाम उस सरणी का तत्व, दूसरा तत्व नहीं। इसलिए, यह मूल वस्तु को इंगित नहीं करता है। यह एक संदर्भ नहीं है, न ही यह उस वस्तु का नाम है।

इसलिए, यह [basic.life]/8 द्वारा दिए गए प्रतिबंधों के लिए योग्य नहीं है। तो x[1] को नव निर्मित वस्तु को इंगित करना चाहिए।

यह देखते हुए कि आपको launder की आवश्यकता नहीं है।

तो यदि आप इसे कानूनी तरीके से कर रहे हैं, तो आपको launder की आवश्यकता नहीं है।

+0

एक सेकंड प्रतीक्षा करें। क्या इसका मतलब है कि सूचक पर पॉइंटर अंकगणित करना 'std :: vector :: डेटा() 'यूबी भी है? जैसा कि यह मूल रूप से एक उदाहरण 1 है: कुछ आंतरिक मूल रूप से अनियमित बफर में बनाए गए वस्तुओं का संग्रह। –

+2

@Revolver_Ocelot निश्चित रूप से। [सीडब्ल्यूजी 2182] देखें (https://wg21.link/CWG2182)। – Barry

+1

@Revolver_Ocelot: नहीं, 'वेक्टर' रिटर्न पर पॉइंटर अंकगणित करना ठीक है। लेकिन ऐसा इसलिए है क्योंकि मानक * स्पष्ट रूप से निर्दिष्ट करता है कि यह ठीक है। 'वेक्टर' मानक पुस्तकालय का हिस्सा है और इसलिए कार्यान्वयन-परिभाषित चीजों को करने की अनुमति है जो उपयोगकर्ताओं के लिए यूबी होगी। संक्षेप में, इस नियम का अर्थ है कि आप कानूनी रूप से 'वेक्टर' को लागू नहीं कर सकते हैं। –

2

कारण std::launder पहली जगह में की जरूरत है, तो एक वस्तु के जीवनकाल के बाद समाप्त हो गया है और से [basic.life]

इस उल्लंघन के कारण है भंडारण जो वस्तु पर कब्जा कर लिया पुन: उपयोग या जारी किया गया है से पहले, एक नई वस्तु भंडारण स्थान पर बनाई गई है जो मूल वस्तु पर कब्जा कर लिया गया है, मूल सूचक को इंगित करने वाला एक सूचक, मूल ऑब्जेक्ट को संदर्भित संदर्भ या मूल ऑब्जेक्ट का नाम स्वचालित रूप से नई ऑब्जेक्ट को संदर्भित करेगा और, एक बार नई वस्तु का जीवनकाल शुरू हो गया है, जिसका उपयोग नई वस्तु में हेरफेर करने के लिए किया जा सकता है, यदि: [...]

  • मूल ऑब्जेक्ट का प्रकार कॉन्स्ट-क्वालिटी नहीं है, और, यदि किसी वर्ग प्रकार में कोई गैर-स्थैतिक डेटा सदस्य नहीं है जिसका प्रकार कॉन्स्ट-क्वालिफाइड या संदर्भ प्रकार है, और [...]

इसलिए std::launder, p बिना मूल सूचक नवनिर्मित वस्तु को इंगित नहीं होता।

इन शर्तों को पूरा नहीं कर रहे हैं, नई वस्तु के लिए सूचक एक सूचक है कि std​::​launder

यही वजह है std::launderwhat it does करता है फोन करके इसके भंडारण के पते का प्रतिनिधित्व से प्राप्त किया जा सकता है।

[ptr.launder] से

, जिसे उपयुक्त शीर्षक सूचक अनुकूलन बाधा

एक नई वस्तु एक ही प्रकार के किसी मौजूदा ऑब्जेक्ट के कब्जे में भंडारण में बनाया गया है तो मूल वस्तु के लिए एक सूचक का उल्लेख किया जा सकता है नई ऑब्जेक्ट जब तक कि प्रकार में कॉन्स या संदर्भ सदस्य न हों; बाद के मामलों में, इस फ़ंक्शन का उपयोग नए ऑब्जेक्ट में उपयोग करने योग्य पॉइंटर प्राप्त करने के लिए किया जा सकता है।

जो कह रहा है कि मूल सूचक का उपयोग नए निर्मित ऑब्जेक्ट को लॉन्डर किए जाने के संदर्भ में नहीं किया जा सकता है।

जो मैं देख सकता हूं, उससे दोनों तरीकों का व्याख्या किया जा सकता है (पूरी तरह से गलत हो सकता है)।

  • एक लॉन्डरिंग की सूचक से गणना की एक संकेतक, मूल सूचक नहीं है इसलिए इसकी अच्छी तरह से गठित
  • कहीं यह उल्लेख किया है कि एक सूचक एक लॉन्डरिंग की सूचक से गणना की लॉन्डरिंग की है, इसलिए इसकी यूबी
है

मैं व्यक्तिगत रूप से सत्य मानता हूं कि std::launder एक सूचक अनुकूलन बाधा होने के कारण।

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