RValues ​​में std :: move () ट्रांसफर वैल्यू कैसे करता है?


104

मैंने सिर्फ अपने आप को तर्क के बारे में पूरी तरह से नहीं समझा std::move()

सबसे पहले, मैंने इसे googled किया, लेकिन ऐसा लगता है कि कैसे उपयोग करने के बारे में केवल दस्तावेज हैं std::move(), न कि इसकी संरचना कैसे काम करती है।

मेरा मतलब है, मुझे पता है कि टेम्पलेट सदस्य फ़ंक्शन क्या है, लेकिन जब मैं std::move()VS2010 में परिभाषा में देखता हूं , तो यह अभी भी भ्रमित है।

एसटीडी की परिभाषा :: चाल () नीचे जाती है।

template<class _Ty> inline
typename tr1::_Remove_reference<_Ty>::_Type&&
    move(_Ty&& _Arg)
    {   // forward _Arg as movable
        return ((typename tr1::_Remove_reference<_Ty>::_Type&&)_Arg);
    }

मेरे लिए सबसे पहले जो अजीब है वह है पैरामीटर, (_Ty && _Arg), क्योंकि जब मैं फ़ंक्शन को कॉल करता हूं, जैसे कि नीचे देखें,

// main()
Object obj1;
Object obj2 = std::move(obj1);

यह मूल रूप से के बराबर है

// std::move()
_Ty&& _Arg = Obj1;

लेकिन जैसा कि आप पहले से ही जानते हैं, आप सीधे LValue को RValue के संदर्भ में लिंक नहीं कर सकते हैं, जिससे मुझे लगता है कि यह इस तरह होना चाहिए।

_Ty&& _Arg = (Object&&)obj1;

हालाँकि, यह बेतुका है क्योंकि std :: move () सभी मूल्यों के लिए काम करना चाहिए।

इसलिए मुझे लगता है कि यह पूरी तरह से कैसे काम करता है, मुझे इन संरचनाओं पर भी एक नज़र डालनी चाहिए।

template<class _Ty>
struct _Remove_reference
{   // remove reference
    typedef _Ty _Type;
};

template<class _Ty>
struct _Remove_reference<_Ty&>
{   // remove reference
    typedef _Ty _Type;
};

template<class _Ty>
struct _Remove_reference<_Ty&&>
{   // remove rvalue reference
    typedef _Ty _Type;
};

दुर्भाग्य से यह अभी भी भ्रामक है और मुझे नहीं मिला।

मुझे पता है कि यह सब C ++ के बारे में बुनियादी वाक्यविन्यास कौशल की कमी के कारण है। मैं जानना चाहता हूं कि ये कैसे पूरी तरह से काम करते हैं और इंटरनेट पर मुझे मिलने वाले किसी भी दस्तावेज का स्वागत करने की तुलना में अधिक होगा। (यदि आप इसे केवल समझा सकते हैं, तो यह बहुत बढ़िया होगा)।


31
@NicolBolas इसके विपरीत, सवाल ही दिखाता है कि ओपी उस बयान में खुद को रेखांकित कर रहा है। यह मूल रूप से बुनियादी वाक्यविन्यास कौशल की ओपी की समझ है जो उन्हें सवाल का जवाब देने की अनुमति देता है।
काइल स्ट्रैंड

मैं वैसे भी असहमत हूं - आप जल्दी से सीख सकते हैं या पहले से ही कंप्यूटर विज्ञान या प्रोग्रामिंग की पूरी समझ है, यहां तक ​​कि सी ++ में भी, और अभी भी वाक्य रचना के साथ संघर्ष करते हैं। मैकेनिक्स, एल्गोरिदम आदि सीखने में अपना समय व्यतीत करना बेहतर है, लेकिन आवश्यकतानुसार सिंटैक्स पर ब्रश करने के लिए संदर्भों पर भरोसा करें, क्योंकि यह तकनीकी रूप से स्पष्ट होना है, लेकिन कॉपी / मूव / फॉरवर्ड जैसी चीजों की उथली समझ रखना है। आपको यह कैसे पता होना चाहिए कि "कब और कैसे std का उपयोग करना है :: चाल जब आप कुछ निर्माण करना चाहते हैं" तो इससे पहले कि आप जानते हैं कि क्या कदम है?
जॉन पी

मुझे लगता है कि मैं यह समझने के लिए यहां आया हूं moveकि यह कैसे लागू होता है, इसके बजाय कैसे काम करता है। मुझे यह स्पष्टीकरण वास्तव में उपयोगी लगता है: pagefault.blog/2018/03/01/…
एंटोन डेनेको

जवाबों:


171

हम मूव फंक्शन से शुरू करते हैं (जिसे मैंने थोड़ा सा साफ किया):

template <typename T>
typename remove_reference<T>::type&& move(T&& arg)
{
  return static_cast<typename remove_reference<T>::type&&>(arg);
}

चलिए आसान भाग से शुरू करते हैं - वह है, जब फ़ंक्शन को रिवाल्यू कहा जाता है:

Object a = std::move(Object());
// Object() is temporary, which is prvalue

और हमारा moveटेम्पलेट निम्नानुसार त्वरित हो जाता है:

// move with [T = Object]:
remove_reference<Object>::type&& move(Object&& arg)
{
  return static_cast<remove_reference<Object>::type&&>(arg);
}

के बाद से remove_referenceधर्मान्तरित T&करने के लिए Tया T&&करने के लिए T, और Objectसंदर्भ में नहीं है, हमारे अंतिम समारोह है:

Object&& move(Object&& arg)
{
  return static_cast<Object&&>(arg);
}

अब, आप आश्चर्यचकित हो सकते हैं: क्या हमें कलाकारों की भी आवश्यकता है? जवाब है: हाँ, हम करते हैं। वजह साफ है; rvalue रेफरेंस को lvalue के रूप में माना जाता है (और lvalue से rvalue संदर्भ में अंतर्निहित रूपांतरण मानक द्वारा निषिद्ध है)।


यहाँ तब होता है जब हम movelvalue के साथ कॉल करते हैं:

Object a; // a is lvalue
Object b = std::move(a);

और इसी moveतात्कालिकता:

// move with [T = Object&]
remove_reference<Object&>::type&& move(Object& && arg)
{
  return static_cast<remove_reference<Object&>::type&&>(arg);
}

फिर से, करने के लिए remove_referenceधर्मान्तरित और हम:Object&Object

Object&& move(Object& && arg)
{
  return static_cast<Object&&>(arg);
}

अब हम मुश्किल भाग में आते हैं: Object& &&इसका क्या मतलब है और यह कैसे बंध सकता है?

सही अग्रेषण की अनुमति देने के लिए, C ++ 11 मानक संदर्भ ढहने के लिए विशेष नियम प्रदान करता है, जो इस प्रकार हैं:

Object &  &  = Object &
Object &  && = Object &
Object && &  = Object &
Object && && = Object &&

जैसा कि आप देख सकते हैं, इन नियमों के तहत Object& &&वास्तव में इसका मतलब है Object&, जो सादे लैवल्यू संदर्भ है जो बाध्यकारी अंतराल की अनुमति देता है।

अंतिम समारोह इस प्रकार है:

Object&& move(Object& arg)
{
  return static_cast<Object&&>(arg);
}

जो प्रतिद्वंद्विता के साथ पिछले तात्कालिकता के विपरीत नहीं है - वे दोनों अपने तर्क को संदर्भ के संदर्भ में डालते हैं और फिर इसे वापस करते हैं। अंतर यह है कि पहले तात्कालिकता का उपयोग केवल rvalues ​​के साथ किया जा सकता है, जबकि दूसरा lvalues ​​के साथ काम करता है।


यह समझाने के लिए कि हमें remove_referenceकुछ और क्यों चाहिए , आइए इस फ़ंक्शन को आज़माएँ

template <typename T>
T&& wanna_be_move(T&& arg)
{
  return static_cast<T&&>(arg);
}

और इसे तुरंत अंतराल के साथ।

// wanna_be_move [with T = Object&]
Object& && wanna_be_move(Object& && arg)
{
  return static_cast<Object& &&>(arg);
}

उपर्युक्त उल्लिखित संदर्भ नियमों को लागू करते हुए, आप देख सकते हैं कि हम फ़ंक्शन प्राप्त कर सकते हैं जो अनुपयोगी है move( जैसा कि इसे सीधे शब्दों में कहें, तो आप इसे लवल्यू के साथ कहते हैं, आपको अंतराल वापस मिल जाता है)। यदि कुछ है, तो यह फ़ंक्शन पहचान फ़ंक्शन है।

Object& wanna_be_move(Object& arg)
{
  return static_cast<Object&>(arg);
}

3
अच्छा उत्तर। हालांकि मैं समझता हूँ कि यह एक अच्छा विचार का मूल्यांकन करना है एक lvalue के लिए Tके रूप में Object&, मुझे नहीं पता था कि यह वास्तव में किया जाता है। मुझे इस मामले में Tभी मूल्यांकन करने की उम्मीद होगी Object, जैसा कि मैंने सोचा था कि यह रैपर संदर्भों को पेश करने का कारण था और std::refयह नहीं था।
क्रिश्चियन राऊ

2
template <typename T> void f(T arg)(जो विकिपीडिया लेख में है) और के बीच अंतर है template <typename T> void f(T& arg)। पहला एक मूल्य के लिए हल करता है (और यदि आप संदर्भ पास करना चाहते हैं, तो आपको इसे लपेटना होगा std::ref), जबकि दूसरा हमेशा संदर्भ के लिए हल होता है। अफसोस की बात है कि, टेम्प्लेट तर्क वितर्क के नियम अधिक जटिल हैं, इसलिए मैं सटीक तर्क नहीं दे पा रहा हूं कि यह क्यों T&&बदलता है Object& &&(लेकिन वास्तव में ऐसा होता है)।
विट्टस

1
लेकिन, क्या कोई कारण है कि यह प्रत्यक्ष दृष्टिकोण काम नहीं करेगा? template <typename T> T&& also_wanna_be_move(T& arg) { return static_cast<T&&>(arg); }
ग्रेगोगो

1
यह बताता है कि क्यों remove_reference आवश्यक है, लेकिन मुझे अभी भी नहीं मिला कि फ़ंक्शन को T && (T & के बजाय) क्यों लेना है। अगर मैं इस स्पष्टीकरण को सही ढंग से समझता हूं, तो इससे कोई फ़र्क नहीं पड़ना चाहिए कि क्या आपको T && या T & मिल रहा है क्योंकि या तो, remove_reference इसे T पर ले जाएगा, तो आप वापस && जोड़ देंगे। तो क्यों नहीं कहते हैं कि आप एक T & को स्वीकार करते हैं (जो शब्दार्थ को आप स्वीकार कर रहे हैं), T && के बजाय और कॉल अग्रेषित करने की अनुमति देने के लिए सही अग्रेषण पर निर्भर है?
मागुका

3
@ मिगुका: यदि आप std::moveकेवल लवलीन को प्रतिद्वंद्वियों में डालना चाहते हैं , तो हाँ, T&ठीक होगा। यह ट्रिक ज्यादातर लचीलेपन के लिए की जाती है: आप std::moveहर चीज पर कॉल कर सकते हैं (जिसमें शामिल हैं) और फिर से रिवैल्यू प्राप्त करें।
वाइटस

4

_ यह एक टेम्पलेट पैरामीटर है, और इस स्थिति में

Object obj1;
Object obj2 = std::move(obj1);

_Ty "ऑब्जेक्ट और" प्रकार है

यही कारण है कि _Remove_reference आवश्यक है।

यह ज्यादा पसंद आएगा

typedef Object& ObjectRef;
Object obj1;
ObjectRef&& obj1_ref = obj1;
Object&& obj2 = (Object&&)obj1_ref;

यदि हम संदर्भ को नहीं हटाते हैं तो ऐसा होगा जैसे हम कर रहे थे

Object&& obj2 = (ObjectRef&&)obj1_ref;

लेकिन ObjectRef &&, Object और को कम करता है, जिसे हम obj2 में बाँध नहीं सकते।

कारण यह इस तरह से कम कर देता है सही अग्रेषण का समर्थन करने के लिए। इस कागज को देखें ।


यह सब कुछ क्यों _Remove_reference_आवश्यक है के बारे में व्याख्या नहीं करता है। उदाहरण के लिए, यदि आपके पास Object&एक टंकण है, और आप इसका संदर्भ लेते हैं, तब भी आपको मिलता है Object&। वह && के साथ क्यों काम नहीं करता है? वहाँ है कि करने के लिए एक जवाब है, और यह सही अग्रेषण से कोई लेना देना नहीं है।
निकोल बोलस

2
सच। और जवाब बहुत दिलचस्प है। A & & A को A & से घटा दिया जाता है, इसलिए यदि हमने (ObjectRef &&) obj1_ref का उपयोग करने का प्रयास किया, तो हमें इस मामले में ऑब्जेक्ट और बदले मिलेगा।
वॉन काटो
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.