`Std :: Move` को` std :: Move` नाम क्यों दिया गया है?


128

C ++ 11 std::move(x)फ़ंक्शन वास्तव में कुछ भी स्थानांतरित नहीं करता है। यह आर-वैल्यू के लिए सिर्फ एक कास्ट है। ऐसा क्यों किया गया? क्या यह भ्रामक नहीं है?


मामलों को बदतर बनाने के लिए, तीन-तर्क std::moveवास्तव में चलता है ..
घनबी

और C ++ std::char_traits::move
98/03/11

30
मेरा दूसरा पसंदीदा वह std::remove()तत्व है जो तत्वों को नहीं हटाता है: आपको अभी भी erase()कंटेनर से उन तत्वों को हटाने के लिए कॉल करना है। तो moveनहीं हटता है, removeनहीं हटाता है। मैं नाम उठाया है | mark_movable()के लिए move
अली

4
@ अगर मुझे mark_movable()भी भ्रम होता । यह बताता है कि एक स्थायी दुष्प्रभाव है जहां वास्तव में कोई नहीं है।
फिन जुवें

जवाबों:


177

यह सही है कि std::move(x)सिर्फ एक कास्ट करने के लिए है - विशेष रूप से एक xvalue करने के लिए , जैसा कि एक प्रचलन के विपरीत । और यह भी सच है कि moveकभी-कभी कलाकारों का नाम लोगों को भ्रमित कर देता है। हालांकि इस नामकरण का इरादा भ्रमित करने के लिए नहीं है, बल्कि आपके कोड को अधिक पठनीय बनाने के लिए है।

2002 में मूल चाल प्रस्ताव पर moveतारीखों का इतिहास । यह पत्र पहले रैवल्यू संदर्भ का परिचय देता है, और फिर दिखाता है कि अधिक कुशल कैसे लिखा जाए :std::swap

template <class T>
void
swap(T& a, T& b)
{
    T tmp(static_cast<T&&>(a));
    a = static_cast<T&&>(b);
    b = static_cast<T&&>(tmp);
}

किसी को याद करना होगा कि इतिहास में इस बिंदु पर, केवल एक चीज जो " &&" का अर्थ संभवतः तार्किक और हो सकता है । कोई भी रेवले के संदर्भ से परिचित नहीं था, और न ही एक रैलिंग में एक कॉवेल को कास्टिंग करने के निहितार्थ (जबकि ऐसा करने के लिए एक प्रति नहीं बना रहा था static_cast<T>(t))। तो इस कोड के पाठक स्वाभाविक रूप से सोचेंगे:

मुझे पता है कि कैसे swapकाम करना चाहिए (अस्थायी रूप से कॉपी करें और फिर मूल्यों का आदान-प्रदान करें), लेकिन उन बदसूरत जातियों का उद्देश्य क्या है ?!

यह भी ध्यान दें कि swapसभी प्रकार के क्रमपरिवर्तन-संशोधन एल्गोरिदम के लिए वास्तव में सिर्फ एक स्टैंड-इन है। यह चर्चा ज्यादा है , बहुत बड़ी है swap

फिर प्रस्ताव प्रस्तुत किया जाने वाक्य रचना चीनी जो बदलता है static_cast<T&&>अधिक पढ़ने योग्य चीज़ के बारे में बताता सटीक नहीं क्या , बल्कि क्यों :

template <class T>
void
swap(T& a, T& b)
{
    T tmp(move(a));
    a = move(b);
    b = move(tmp);
}

यानी के moveलिए सिर्फ वाक्यविन्यास चीनी है static_cast<T&&>, और अब कोड काफी विचारोत्तेजक है कि क्यों उन जातियों में हैं: स्थानांतरित शब्दार्थ को सक्षम करने के लिए!

यह समझना चाहिए कि इतिहास के संदर्भ में, इस बिंदु पर कुछ लोगों ने वास्तव में प्रतिद्वंद्वियों और चाल शब्दार्थों के बीच अंतरंग संबंध को समझा (हालांकि पेपर यह समझाने की कोशिश करता है कि:)

मूवमेंट तर्क दिए जाने पर स्वचालित रूप से चलन में आ जाएंगे। यह पूरी तरह से सुरक्षित है क्योंकि एक प्रतिद्वंद्विता से चलते हुए संसाधनों को कार्यक्रम के बाकी हिस्सों द्वारा नहीं देखा जा सकता है ( किसी और के पास किसी अंतर का पता लगाने के लिए प्रतिद्वंद्विता का संदर्भ नहीं है )।

यदि उस समय swapइस तरह प्रस्तुत किया गया था:

template <class T>
void
swap(T& a, T& b)
{
    T tmp(cast_to_rvalue(a));
    a = cast_to_rvalue(b);
    b = cast_to_rvalue(tmp);
}

तब लोगों ने उस पर नज़र डाली और कहा:

लेकिन आप प्रतिद्वंद्विता क्यों कर रहे हैं?


मुख्य बिंदु:

जैसा कि यह था, उपयोग करना move, किसी ने कभी नहीं पूछा:

लेकिन आप क्यों बढ़ रहे हैं?


जैसे-जैसे साल बीतते गए और प्रस्ताव को परिष्कृत किया गया, आज के मूल्य वर्गों में अंतराल और प्रतिद्वंद्विता की धारणाओं को परिष्कृत किया गया :

वर्गीकरण

(छवि बेशर्म से चोरी dirkgently )

और इसलिए आज, अगर हम swapठीक से कहना चाहते थे कि यह क्या कर रहा है, इसके बजाय , इसे अधिक क्यों दिखना चाहिए:

template <class T>
void
swap(T& a, T& b)
{
    T tmp(set_value_category_to_xvalue(a));
    a = set_value_category_to_xvalue(b);
    b = set_value_category_to_xvalue(tmp);
}

और यह सवाल हर किसी को खुद से पूछना चाहिए कि क्या उपरोक्त कोड कमोबेश पठनीय है:

template <class T>
void
swap(T& a, T& b)
{
    T tmp(move(a));
    a = move(b);
    b = move(tmp);
}

या मूल भी:

template <class T>
void
swap(T& a, T& b)
{
    T tmp(static_cast<T&&>(a));
    a = static_cast<T&&>(b);
    b = static_cast<T&&>(tmp);
}

किसी भी घटना में, यात्री सी ++ प्रोग्रामर को पता होना चाहिए कि हुड के तहत move, कलाकारों की तुलना में अधिक कुछ नहीं चल रहा है। और शुरुआती सी ++ प्रोग्रामर, कम से कम move, के साथ सूचित किया जाएगा कि इरादे आरएचएस से स्थानांतरित करने के लिए है, जैसा कि आरएस से कॉपी करने के लिए विरोध किया जाता है , भले ही वे यह नहीं समझते कि यह कैसे पूरा होता है।

इसके अतिरिक्त, यदि कोई प्रोग्रामर किसी अन्य नाम के तहत इस कार्यक्षमता की इच्छा रखता है, तो इस कार्यक्षमता std::moveपर कोई एकाधिकार नहीं है, और इसके कार्यान्वयन में कोई गैर-पोर्टेबल भाषा जादू शामिल नहीं है। उदाहरण के लिए यदि कोई कोड करना चाहता था set_value_category_to_xvalue, और इसके बजाय उसका उपयोग करना चाहता है , तो ऐसा करना तुच्छ है:

template <class T>
inline
constexpr
typename std::remove_reference<T>::type&&
set_value_category_to_xvalue(T&& t) noexcept
{
    return static_cast<typename std::remove_reference<T>::type&&>(t);
}

C ++ 14 में यह और भी अधिक संक्षिप्त हो जाता है:

template <class T>
inline
constexpr
auto&&
set_value_category_to_xvalue(T&& t) noexcept
{
    return static_cast<std::remove_reference_t<T>&&>(t);
}

इसलिए यदि आप इतने झुके हुए हैं, तो अपने सजने-संवरने को static_cast<T&&>सबसे अच्छा समझें, और शायद आप एक नए सर्वोत्तम अभ्यास को विकसित करेंगे (C ++ लगातार विकसित हो रहा है)।

तो moveउत्पन्न वस्तु कोड के संदर्भ में क्या करता है?

इस पर विचार करें test:

void
test(int& i, int& j)
{
    i = j;
}

के साथ संकलित clang++ -std=c++14 test.cpp -O3 -S, यह इस ऑब्जेक्ट कोड का उत्पादन करता है:

__Z4testRiS_:                           ## @_Z4testRiS_
    .cfi_startproc
## BB#0:
    pushq   %rbp
Ltmp0:
    .cfi_def_cfa_offset 16
Ltmp1:
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
Ltmp2:
    .cfi_def_cfa_register %rbp
    movl    (%rsi), %eax
    movl    %eax, (%rdi)
    popq    %rbp
    retq
    .cfi_endproc

अब यदि परीक्षण को बदल दिया जाए:

void
test(int& i, int& j)
{
    i = std::move(j);
}

नहीं है बिल्कुल सब पर कोई परिवर्तन वस्तु कोड में। कोई भी इस परिणाम को सामान्य कर सकता है: तुच्छ चल वस्तुओं के लिए, std::moveकोई प्रभाव नहीं है।

अब इस उदाहरण को देखते हैं:

struct X
{
    X& operator=(const X&);
};

void
test(X& i, X& j)
{
    i = j;
}

यह उत्पन्न करता है:

__Z4testR1XS0_:                         ## @_Z4testR1XS0_
    .cfi_startproc
## BB#0:
    pushq   %rbp
Ltmp0:
    .cfi_def_cfa_offset 16
Ltmp1:
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
Ltmp2:
    .cfi_def_cfa_register %rbp
    popq    %rbp
    jmp __ZN1XaSERKS_           ## TAILCALL
    .cfi_endproc

यदि आप इसके __ZN1XaSERKS_माध्यम से चलते हैं c++filtतो उत्पादन होता है X::operator=(X const&):। यहां कोई आश्चर्य नहीं। अब यदि परीक्षण को बदल दिया जाए:

void
test(X& i, X& j)
{
    i = std::move(j);
}

फिर जेनरेट किए गए ऑब्जेक्ट कोड में अभी भी कोई बदलाव नहीं हुआ है। std::moveकुछ भी नहीं किया है, लेकिन jएक प्रतिद्वंद्विता के लिए डाली है, और फिर उस प्रतिद्वंद्विता Xकी प्रतिलिपि असाइनमेंट ऑपरेटर को बांधता है X

अब एक चाल असाइनमेंट ऑपरेटर को इसमें जोड़ने देता है X:

struct X
{
    X& operator=(const X&);
    X& operator=(X&&);
};

अब वस्तु कोड करता है परिवर्तन:

__Z4testR1XS0_:                         ## @_Z4testR1XS0_
    .cfi_startproc
## BB#0:
    pushq   %rbp
Ltmp0:
    .cfi_def_cfa_offset 16
Ltmp1:
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
Ltmp2:
    .cfi_def_cfa_register %rbp
    popq    %rbp
    jmp __ZN1XaSEOS_            ## TAILCALL
    .cfi_endproc

के बजाय बुलाया जा रहा है कि पता चलता है के __ZN1XaSEOS_माध्यम से चल रहा है ।c++filtX::operator=(X&&)X::operator=(X const&)

और यह सब वहाँ है std::move! यह रन टाइम पर पूरी तरह से गायब हो जाता है। इसका एकमात्र प्रभाव संकलन-समय पर है जहां यह परिवर्तन हो सकता है कि अधिभार क्या कहलाता है।


7
यहाँ उस ग्राफ़ के लिए एक डॉट स्रोत है: मैंने इसे digraph D { glvalue -> { lvalue; xvalue } rvalue -> { xvalue; prvalue } expression -> { glvalue; rvalue } }जनता की भलाई के लिए फिर से बनाया :) इसे यहाँ
sehe

7
क्या यह अभी भी बिकेशिंग के लिए खुला है? मैं सुझाव देता हूं allow_move;)
dyp

2
@dyp मेरा पसंदीदा अभी भी है movable
डैनियल फ़्री

6
स्कॉट मेयर्स ने नाम बदलने std::moveका सुझाव दिया rvalue_cast: youtube.com/…
nairware

6
चूँकि अब प्रतिद्वंद्विता प्रचलन और xvalues ​​दोनों को संदर्भित करती है, rvalue_castअपने अर्थ में अस्पष्ट है: किस प्रकार का अंतराल वापस नहीं आता है? xvalue_castयहाँ एक सुसंगत नाम होगा। दुर्भाग्य से, अधिकांश लोग, इस समय, यह भी नहीं समझ पाएंगे कि यह क्या कर रहा है। कुछ और वर्षों में, मेरा बयान उम्मीद से गलत हो जाएगा।
हावर्ड हिनान्ट

20

मुझे बी। स्ट्रॉस्ट्रुप द्वारा लिखे गए C ++ 11 FAQ से सिर्फ एक उद्धरण यहाँ छोड़ना है , जो ओपी के प्रश्न का सीधा उत्तर है:

चाल (एक्स) का अर्थ है "आप एक्स को एक प्रतिद्वंद्विता के रूप में मान सकते हैं"। शायद यह बेहतर होता अगर चाल () को प्रतिद्वंद्वी कहा जाता था (), लेकिन अब तक कदम () का उपयोग वर्षों से किया जाता रहा है।

वैसे, मैंने अक्सर FAQ का आनंद लिया - यह पढ़ने लायक है।


2
एक अन्य जवाब से @ हावर्डहाइंट की टिप्पणी को टालने के लिए: स्ट्रॉस्ट्रुप उत्तर गलत है, क्योंकि अब दो प्रकार के प्रचलन हैं - प्रचलन और xvalues, और std :: चाल वास्तव में एक xvalue कास्ट है।
ईनपोक्लुम 19
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.