हम एक 'const' ऑब्जेक्ट पर `std :: Move` का उपयोग क्यों कर सकते हैं?


113

C ++ 11 में, हम इस कोड को लिख सकते हैं:

struct Cat {
   Cat(){}
};

const Cat cat;
std::move(cat); //this is valid in C++11

जब मैं कॉल करता हूं std::move, तो इसका मतलब है कि मैं ऑब्जेक्ट को स्थानांतरित करना चाहता हूं, अर्थात मैं ऑब्जेक्ट को बदल दूंगा। किसी constवस्तु को स्थानांतरित करना अनुचित है, इसलिए std::moveइस व्यवहार को प्रतिबंधित क्यों नहीं किया जाता है? यह भविष्य में एक जाल होगा, है ना?

यहाँ ट्रैप का मतलब ब्रैंडन की टिप्पणी में बताया गया है:

"मुझे लगता है कि इसका मतलब है कि वह उसे फँसाता है" डरपोक है क्योंकि अगर उसे एहसास नहीं होता है, तो वह एक कॉपी के साथ समाप्त होता है जो कि उसका इरादा नहीं है। "

स्कॉट मेयर्स की पुस्तक 'इफेक्टिव मॉडर्न C ++' में वह एक उदाहरण देते हैं:

class Annotation {
public:
    explicit Annotation(const std::string text)
     : value(std::move(text)) //here we want to call string(string&&),
                              //but because text is const, 
                              //the return type of std::move(text) is const std::string&&
                              //so we actually called string(const string&)
                              //it is a bug which is very hard to find out
private:
    std::string value;
};

यदि std::moveकिसी constवस्तु पर काम करने से मना किया गया था , तो हम आसानी से बग का पता लगा सकते हैं?


2
लेकिन इसे स्थानांतरित करने का प्रयास करें। इसकी अवस्था को बदलने का प्रयास करें। std::moveअपने आप से वस्तु के लिए कुछ नहीं करता है। कोई बहस कर सकता हैstd::move कि उसका नाम खराब है।
जुआनकोपांजा

3
यह वास्तव में कुछ भी स्थानांतरित नहीं करता है। यह सब एक प्रतिद्वंद्विता संदर्भ के लिए किया जाता है। कोशिश करें CAT cat2 = std::move(cat);, मान लें कि CATनियमित चाल-असाइनमेंट का समर्थन करता है।
WhozCraig

11
std::moveसिर्फ एक कलाकार है, यह वास्तव में कुछ भी नहीं ले जाता है
लाल चेतावनी

2
@WhozCraig: सावधान, जिस कोड को आपने संकलित किया है और बिना किसी चेतावनी के निष्पादित करता है, वह इसे भ्रामक बनाता है।
मूविंग डक

1
@MingDuck ने कभी नहीं कहा कि यह संकलन नहीं करेगा। यह केवल इसलिए काम करता है क्योंकि डिफ़ॉल्ट कॉपी-कोटर सक्षम है। स्क्वेलच और वो पहिए गिर जाते हैं।
WhozCraig

जवाबों:


55
struct strange {
  mutable size_t count = 0;
  strange( strange const&& o ):count(o.count) { o.count = 0; }
};

const strange s;
strange s2 = std::move(s);

यहाँ हम का एक प्रयोग को देखने के std::moveलिए एक पर T const। यह एक रिटर्न T const&&। हमारे पास एक मूव कंस्ट्रक्टर है strangeजो इस प्रकार का है।

और इसे कहा जाता है।

अब, यह सच है कि यह अजीब प्रकार बग से अधिक दुर्लभ है जो आपके प्रस्ताव को ठीक करेगा।

लेकिन, दूसरी ओर, मौजूदा std::moveकोड जेनेरिक कोड में बेहतर काम करता है, जहां आपको पता नहीं है कि आप जिस प्रकार के साथ काम कर रहे हैं वह एक Tया एक है T const


3
+1 पहला उत्तर होने के लिए जो वास्तव में यह समझाने का प्रयास करता है कि आप किसी वस्तु पर कॉल क्यों करना चाहते हैंstd::moveconst
क्रिस ड्रू

फंक्शन लेने के लिए +1 const T&&। यह "एपीआई प्रोटोकॉल" को व्यक्त करता है "मैं एक प्रतिद्वंद्विता-रेफ ले लूंगा लेकिन मैं वादा करता हूं कि मैं इसे संशोधित नहीं करूंगा"। मुझे लगता है, इसके अलावा जब उत्परिवर्तनीय का उपयोग करते हैं, तो यह असामान्य है। शायद एक और उपयोग मामला forward_as_tupleलगभग किसी भी चीज पर उपयोग करने में सक्षम हो और बाद में इसका उपयोग करें।
एफ परेरा

107

यहाँ एक चाल है जो आप देख रहे हैं, अर्थात् जो std::move(cat) वास्तव में कुछ भी स्थानांतरित नहीं करता है । यह केवल संकलक को स्थानांतरित करने का प्रयास करने के लिए कहता है । हालाँकि, चूंकि आपकी कक्षा में कोई भी निर्माता नहीं है जो const CAT&&इसे स्वीकार करता है , इसलिए यह अंतर्निहित const CAT&कॉपी निर्माता और सुरक्षित रूप से प्रतिलिपि का उपयोग करेगा । कोई खतरा नहीं, कोई जाल नहीं। यदि किसी कारण से कॉपी कंस्ट्रक्टर अक्षम है, तो आपको एक कंपाइलर त्रुटि मिलेगी।

struct CAT
{
   CAT(){}
   CAT(const CAT&) {std::cout << "COPY";}
   CAT(CAT&&) {std::cout << "MOVE";}
};

int main() {
    const CAT cat;
    CAT cat2 = std::move(cat);
}

प्रिंट COPY, नहीं MOVE

http://coliru.stacked-crooked.com/a/0dff72133dbf9d1f

ध्यान दें कि आपके द्वारा उल्लेखित कोड में बग एक प्रदर्शन समस्या है, न कि स्थिरता का मुद्दा, इसलिए इस तरह के बग से दुर्घटना नहीं होगी। यह सिर्फ एक धीमी प्रतिलिपि का उपयोग करेगा। इसके अतिरिक्त, इस तरह की बग गैर-कांस्टेबल ऑब्जेक्ट्स के लिए भी होती है, जिसमें कंस्ट्रक्टर नहीं होते हैं, इसलिए केवल एक constअधिभार जोड़ने से उन सभी को नहीं पकड़ा जाएगा। हम पैरामीटर प्रकार से निर्माण को स्थानांतरित करने या असाइन करने की क्षमता की जांच कर सकते हैं, लेकिन यह जेनेरिक टेम्पलेट कोड के साथ हस्तक्षेप करेगा जो कॉपी कंस्ट्रक्टर पर वापस गिरने वाला है। और बिल्ली, शायद किसी से निर्माण करने में सक्षम होना चाहता है const CAT&&, मैं कौन हूं कहने के लिए वह नहीं कर सकता है?


Upticked। नियमित चाल-निर्माण या असाइनमेंट ऑपरेटर की उपयोगकर्ता-परिभाषित परिभाषा पर कॉपी-कॉटर के निहित विलोपन को ध्यान में रखते हुए वर्थ भी टूटे हुए संकलन के माध्यम से इसे प्रदर्शित करेगा। अच्छा उत्तर।
WhozCraig

यह भी ध्यान देने योग्य है कि एक कॉपी-कंस्ट्रक्टर जिसे गैर- constलैवल्यू की आवश्यकता होती है, वह भी मदद नहीं करेगा। [class.copy] :8: "अन्यथा, अंतर्निहित रूप से घोषित कॉपी कंस्ट्रक्टर का फॉर्म होगा X::X(X&)"
डेडुप्लिकेटर

9
मुझे नहीं लगता कि वह कंप्यूटर / असेंबली शब्दों में "ट्रैप" था। मुझे लगता है कि वह इसका मतलब है कि उसे "फँसा" उसे डरपोक लगता है क्योंकि अगर उसे एहसास नहीं होता है, तो वह एक प्रति के साथ समाप्त होता है जो कि उसका इरादा नहीं है। मुझे लगता है ..
ब्रैंडन

हो सकता है कि आपको 1 और अपवोट मिले, और मुझे गोल्ड बैज के लिए 4 और मिले। ;)
यक - एडम नेवरामोंट

उस कोड नमूने पर उच्च पांच। वास्तव में अच्छी तरह से बिंदु हो जाता है।
ब्रेंट राइट्स कोड

20

एक कारण यह है कि बाकी के जवाबों ने अब तक अनदेखी की है कि जेनेरिक कोड को स्थानांतरित करने की क्षमता में लचीलापन है। उदाहरण के लिए, मैं कहता हूं कि मैं एक सामान्य फ़ंक्शन लिखना चाहता था, जो सभी प्रकार के तत्वों को एक ही प्रकार के कंटेनर से एक ही प्रकार के कंटेनर बनाने के लिए स्थानांतरित कर देता है:

template <class C1, class C2>
C1
move_each(C2&& c2)
{
    return C1(std::make_move_iterator(c2.begin()),
              std::make_move_iterator(c2.end()));
}

कूल, अब मैं अपेक्षाकृत vector<string>एक deque<string>और प्रत्येक व्यक्ति से कुशलता से बना सकता हूंstring को इस प्रक्रिया में स्थानांतरित किया जाएगा।

लेकिन क्या होगा अगर मैं एक से स्थानांतरित करना चाहता हूं map?

int
main()
{
    std::map<int, std::string> m{{1, "one"}, {2, "two"}, {3, "three"}};
    auto v = move_each<std::vector<std::pair<int, std::string>>>(m);
    for (auto const& p : v)
        std::cout << "{" << p.first << ", " << p.second << "} ";
    std::cout << '\n';
}

अगर std::moveएक गैर पर जोर दिया constतर्क, के ऊपर इन्स्टेन्शियशन move_eachसंकलन नहीं होगा, क्योंकि यह एक ले जाने के लिए कोशिश कर रहा है const int( key_typeके map)। लेकिन यह कोड परवाह नहीं करता है अगर यह स्थानांतरित नहीं कर सकता है key_type। यह एक स्थानांतरित करना चाहता है mapped_type(std::string प्रदर्शन कारणों ) ।

यह इस उदाहरण के लिए है, और अनगिनत अन्य उदाहरण जेनेरिक कोडिंग में इसे पसंद करते हैं जो स्थानांतरित करने के लिएstd::move एक अनुरोध है , स्थानांतरित करने की मांग नहीं है।


2

मुझे ओपी की भी उतनी ही चिंता है।

std :: Move एक ऑब्जेक्ट को स्थानांतरित नहीं करता है, न ही गारंटी देता है कि ऑब्जेक्ट मूवेबल है। फिर इसे चाल क्यों कहा जाता है?

मुझे लगता है कि चल सकने योग्य नहीं दो परिदृश्यों में से एक हो सकता है:

1. मूविंग टाइप कांस्टेबल है।

भाषा में हमारे पास कॉन्स्टेबल कीवर्ड होने का कारण यह है कि हम चाहते हैं कि कंपाइलर किसी भी बदलाव को रोकने के लिए कॉन्स्टेबल हो। स्कॉट मेयर्स की पुस्तक में उदाहरण दिया गया है:

    class Annotation {
    public:
     explicit Annotation(const std::string text)
     : value(std::move(text)) // "move" text into value; this code
     {  } // doesn't do what it seems to!    
     
    private:
     std::string value;
    };

इसका शाब्दिक अर्थ क्या है? मूल्य सदस्य के लिए एक कास्ट स्ट्रिंग ले जाएँ - कम से कम, यह मेरी समझ है इससे पहले कि मैं स्पष्टीकरण पढ़ूं।

अगर भाषा का इरादा कदम नहीं उठाने का है या नहीं ले जाने का है तो std :: move () कहा जाता है, तो यह शब्द चाल का उपयोग करते समय भ्रामक है।

अगर भाषा लोगों को std का उपयोग करने के लिए प्रोत्साहित कर रही है :: बेहतर दक्षता प्राप्त करने के लिए कदम, तो इस तरह के जाल को जल्द से जल्द रोकना होगा, विशेष रूप से इस प्रकार के स्पष्ट शाब्दिक विरोधाभास के लिए।

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

2. ऑब्जेक्ट में कोई मूव कंस्ट्रक्टर नहीं है

व्यक्तिगत रूप से, मुझे लगता है कि यह ओपी की चिंता से अलग कहानी है, जैसा कि क्रिस ड्रू ने कहा

@hvd मुझे एक गैर-तर्क की तरह लगता है। सिर्फ इसलिए कि ओपी का सुझाव दुनिया के सभी बगों को ठीक नहीं करता है, इसका मतलब यह नहीं है कि यह एक बुरा विचार है (यह शायद है, लेकिन इस कारण से नहीं कि आप)। - क्रिस ड्रू


1

मुझे आश्चर्य है कि किसी ने भी इसके पिछड़ेपन के अनुकूल पहलू का उल्लेख नहीं किया। मेरा मानना ​​है कि std::moveजानबूझकर C ++ 11 में ऐसा करने के लिए डिज़ाइन किया गया था। कल्पना कीजिए कि आप एक विरासत कोडबेस के साथ काम कर रहे हैं, जो कि C ++ 98 पुस्तकालयों पर बहुत अधिक निर्भर करता है, इसलिए कॉपी असाइनमेंट पर कमबैक के बिना, चलती चीजों को तोड़ देगा।


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