C ++ में शब्दार्थ को स्थानांतरित करें - स्थानीय चर का मूवमेंट-रिटर्न


11

मेरी समझ यह है कि C ++ 11 में, जब आप किसी फ़ंक्शन से मान द्वारा स्थानीय चर लौटाते हैं, तो संकलक को उस चर को r- मान संदर्भ के रूप में मानने की अनुमति दी जाती है और इसे वापस करने के लिए फ़ंक्शन से बाहर ले जाते हैं (यदि RVO / NRVO इसके बजाय, निश्चित रूप से नहीं होता है)।

मेरा सवाल है, क्या यह मौजूदा कोड को नहीं तोड़ सकता?

निम्नलिखित कोड पर विचार करें:

#include <iostream>
#include <string>

struct bar
{
  bar(const std::string& str) : _str(str) {}
  bar(const bar&) = delete;
  bar(bar&& other) : _str(std::move(other._str)) {other._str = "Stolen";}
  void print() {std::cout << _str << std::endl;}

  std::string _str;
};

struct foo
{
  foo(bar& b) : _b(b) {}
  ~foo() {_b.print();}

  bar& _b;
};

bar foobar()
{
  bar b("Hello, World!");
  foo f(b);

  return std::move(b);
}

int main()
{
  foobar();
  return EXIT_SUCCESS;
}

मेरे विचार थे कि किसी स्थानीय वस्तु को नष्ट करने वाले के लिए यह संभव होगा कि वह उस वस्तु को संदर्भित करे जो अव्यवस्थित रूप से स्थानांतरित हो, और इसलिए अप्रत्याशित रूप से एक 'खाली' वस्तु को देखें। मैं इस परीक्षण करने के लिए (देखें कोशिश की http://ideone.com/ZURoeT ), लेकिन मैं स्पष्ट बिना 'सही' परिणाम मिला std::moveमें foobar()। मैं अनुमान लगा रहा हूं कि NRVO के कारण था, लेकिन मैंने उस अक्षम करने के लिए कोड को पुनर्व्यवस्थित करने की कोशिश नहीं की।

क्या मैं सही हूं कि यह परिवर्तन (फ़ंक्शन से बाहर जाने के कारण) स्पष्ट रूप से होता है और मौजूदा कोड को तोड़ सकता है?

अद्यतन यहाँ एक उदाहरण है जो दिखाता है कि मैं किस बारे में बात कर रहा हूँ। निम्नलिखित दो लिंक एक ही कोड के लिए हैं। http://ideone.com/4GFIRu - C ++ 03 http://ideone.com/FcL2Xj - C ++ 11

यदि आप आउटपुट को देखते हैं, तो यह अलग है।

इसलिए, मुझे लगता है कि यह प्रश्न अब बन जाता है, क्या मानक के लिए निहित कदम जोड़ते समय इस पर विचार किया गया था, और यह तय किया गया था कि इस ब्रेकिंग परिवर्तन को जोड़ना ठीक था क्योंकि इस तरह का कोड काफी दुर्लभ है? मुझे आश्चर्य भी है कि क्या कोई संकलक इस तरह के मामलों में चेतावनी देगा ...


एक चाल हमेशा एक विनाशकारी स्थिति में वस्तु को छोड़ना चाहिए।
ज़ैन लिंक्स

हाँ, लेकिन यह सवाल नहीं है। प्री-सी ++ 11 कोड मान सकता है कि एक स्थानीय चर का मूल्य केवल इसे वापस करने के कारण नहीं बदलेगा, इसलिए यह निहित कदम उस धारणा को तोड़ सकता है।
Bwmat

यही मैंने अपने उदाहरण में स्पष्ट करने की कोशिश की; विध्वंसकों के माध्यम से आप 'स्टेटमेंट ऑफ द फंक्शन स्टेटमेंट' के बाद एक फंक्शन के लोकल वैरिएबल की स्थिति का निरीक्षण कर सकते हैं, लेकिन फंक्शन से पहले वास्तव में वापस आ जाते हैं।
Bwmat

यह आपके द्वारा जोड़े गए उदाहरण के साथ एक उत्कृष्ट प्रश्न है। मुझे उम्मीद है कि इसे पेशेवरों से अधिक जवाब मिलेंगे जो इसे स्पष्ट कर सकते हैं। केवल वास्तविक प्रतिक्रिया मैं दे सकता हूं: यही कारण है कि वस्तुओं को आमतौर पर डेटा में गैर-मालिकाना विचार नहीं होना चाहिए। वास्तव में निर्दोष दिखने वाले कोड को लिखने के कई तरीके हैं जब आप वस्तुओं को गैर-स्वामित्व वाले विचार (कच्चे संकेत या संदर्भ) देते हैं यदि आप चाहें तो मैं इसका उचित उत्तर दे सकता हूं, लेकिन मैं अनुमान लगा रहा हूं कि आप वह नहीं हैं जिसके बारे में आप वास्तव में सुनना चाहते हैं। और btw, यह पहले से ही ज्ञात है कि 11 मौजूदा कोड को तोड़ सकते हैं, जैसे नए कीवर्ड को परिभाषित करके।
२०:२० पर Nir Friedman

हाँ, मुझे पता है कि C ++ 11 ने कभी भी किसी पुराने कोड को नहीं तोड़ने का दावा किया है, लेकिन यह बहुत सूक्ष्म है, और वास्तव में याद करने में आसान होगा (कोई संकलक त्रुटियों, चेतावनियों, segfaults)
Bwmat

जवाबों:


8

स्कॉट मेयर्स ने comp.lang.c ++ (अगस्त 2010) में एक समस्या के बारे में पोस्ट किया, जहां चाल निर्माणकर्ताओं की निहित पीढ़ी C ++ 03 वर्ग के आक्रमणकारियों को तोड़ सकती है:

struct X
{
  // invariant: v.size() == 5
  X() : v(5) {}

  ~X() { std::cout << v[0] << std::endl; }

private:    
  std::vector<int> v;
};

int main()
{
    std::vector<X> y;
    y.push_back(X()); // X() rvalue: copied in C++03, moved in C++0x
}

यहाँ समस्या यह है कि C ++ 03 में, Xएक अपरिवर्तनीय था कि इसके vसदस्य में हमेशा 5 तत्व थे। X::~X()उस अपरिवर्तनीय पर गिना जाता है, लेकिन नव-चालित चाल निर्माणकर्ता से चले गए v, जिससे इसकी लंबाई शून्य हो गई।

यह आपके उदाहरण से संबंधित है क्योंकि टूटे हुए आक्रमणकर्ता को केवल X's विध्वंसक' के रूप में जाना जाता है (जैसा कि आप कहते हैं कि किसी स्थानीय वस्तु के विनाशकर्ता के लिए यह संभव है कि उस वस्तु को संदर्भित किया जाए जो अंतर्निहित रूप से स्थानांतरित हो जाए, और इसलिए अप्रत्याशित रूप से एक खाली वस्तु देखें )।

C ++ 11 मौजूदा कोड को तोड़ने और मूव कन्स्ट्रक्टर्स के आधार पर उपयोगी अनुकूलन प्रदान करने के बीच संतुलन हासिल करने की कोशिश करता है।

समिति ने शुरू में तय किया कि निर्माणकर्ता और चाल असाइनमेंट ऑपरेटरों को उपयोगकर्ता द्वारा प्रदान नहीं किए जाने पर कंपाइलर द्वारा उत्पन्न किया जाना चाहिए।

फिर फैसला किया कि यह वास्तव में अलार्म का कारण था और इसने चाल निर्माणकर्ताओं और चाल असाइनमेंट ऑपरेटरों की स्वचालित पीढ़ी को इस तरह से प्रतिबंधित कर दिया कि यह बहुत कम संभावना है, हालांकि असंभव नहीं है, मौजूदा कोड को तोड़ने के लिए (जैसे स्पष्ट रूप से परिभाषित विध्वंसक)।

यह सोचना आकर्षक है कि जब उपयोगकर्ता परिभाषित डिस्ट्रक्टर मौजूद होता है तो निहित चाल निर्माणकर्ताओं की पीढ़ी को रोकना काफी होता है, लेकिन यह सच नहीं है ( N3153 - लागू चाल आगे के विवरण के लिए जाना चाहिए )।

में N3174 - ले जाएँ या नहीं स्थानांतरित करने के लिए Stroupstrup का कहना है:

मैं इसे एक साधारण बैकवर्ड संगतता समस्या के बजाय एक भाषा डिजाइन समस्या मानता हूं। पुराने कोड को तोड़ने से बचना आसान है (जैसे कि C ++ 0x से केवल मूव ऑपरेशंस को हटाएं), लेकिन मुझे C ++ 0x को एक बेहतर भाषा बनाते हुए मूव ऑपरेशंस को एक बड़ा लक्ष्य बनाना है, जिसके लिए यह कुछ C + को तोड़ने लायक हो सकता है। +98 कोड।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.