Std :: Move (), और इसका उपयोग कब किया जाना चाहिए?


655
  1. यह क्या है?
  2. यह क्या करता है?
  3. इसका उपयोग कब किया जाना चाहिए?

अच्छे लिंक की सराहना की जाती है।




12
इस सवाल का जिक्र है std::move(T && t); वहाँ भी मौजूद है std::move(InputIt first, InputIt last, OutputIt d_first)जो एक एल्गोरिथ्म से संबंधित है std::copy। मैं यह इंगित करता हूं कि अन्य लोग उतने भ्रमित नहीं हैं जितना मैं तब था जब पहली बार std::moveतीन तर्कों के साथ सामना किया गया था । en.cppreference.com/w/cpp/algorithm/move
josaphatv

जवाबों:


285

विकिपीडिया पृष्ठ C ++ 11 R- मूल्य संदर्भों पर और बिल्डरों को स्थानांतरित करें

  1. C ++ 11 में, कॉपी कंस्ट्रक्टर्स के अलावा, ऑब्जेक्ट्स में कंस्ट्रक्टर्स मूव हो सकते हैं।
    (और कॉपी असाइनमेंट ऑपरेटरों के अलावा, उनके पास असाइनमेंट ऑपरेटर हैं।)
  2. मूव कंस्ट्रक्टर का उपयोग कॉपी कंस्ट्रक्टर के बजाय किया जाता है, यदि ऑब्जेक्ट में "रूवेल्यू-रेफरेंस" ( Type &&) है।
  3. std::move() एक कास्ट है जो किसी वस्तु से एक रवु-संदर्भ पैदा करता है, जिससे वह आगे बढ़ सके।

यह कॉपियों से बचने का एक नया C ++ तरीका है। उदाहरण के लिए, एक मूव कंस्ट्रक्टर का उपयोग करते हुए, std::vectorअपने आंतरिक पॉइंटर को डेटा को नई ऑब्जेक्ट में कॉपी कर सकता है, स्थानांतरित वस्तु को राज्य से स्थानांतरित करने में छोड़ देता है, इसलिए सभी डेटा की प्रतिलिपि नहीं बनाता है। यह C ++ - मान्य होगा।

चाल शब्दार्थ, प्रतिद्वंद्विता, सही अग्रेषण के लिए googling का प्रयास करें।


39
मूव-शब्दार्थ को ले जाने वाले ऑब्जेक्ट को वैध बने रहने की आवश्यकता होती है , जो कि एक गलत स्थिति नहीं है। (तर्क: यह अभी भी विनाश करना है, इसे काम करने के लिए।)
GManNickG

13
@ मन: अच्छी तरह से, यह एक ऐसी स्थिति में होना है जो विनाश के लिए सुरक्षित है, लेकिन, AFAIK, इसे किसी और चीज के लिए उपयोग करने योग्य नहीं है।
झेन लिंक्स

8
@ZanLynx: सही है। ध्यान दें कि मानक पुस्तकालय को अतिरिक्त रूप से ले जाने वाली वस्तुओं की आवश्यकता होती है, लेकिन यह केवल स्टैडलिब में प्रयुक्त वस्तुओं के लिए है, न कि सामान्य आवश्यकता के लिए।
GManNickG

24
-1 "std :: Move () मूवमेंट शब्दार्थ का उपयोग करने का C ++ 11 तरीका है" कृपया इसे ठीक करें। std::move()चाल शब्दार्थों का उपयोग करने का तरीका नहीं है, स्थानांतरित शब्दार्थ को पारदर्शी रूप से प्रोग्रामर को दिया जाता है। moveइसका केवल एक कास्ट एक मूल्य से दूसरे बिंदु पर पास करने के लिए जहां मूल अंतराल का उपयोग नहीं किया जाएगा।
मनु ३४३ Man२६

15
मैं और आगे जाता हूँ। std::moveस्वयं "कुछ नहीं" करता है - इसके शून्य दुष्प्रभाव हैं। यह सिर्फ संकलक को संकेत देता है कि प्रोग्रामर को इस बात की परवाह नहीं है कि उस वस्तु का क्या होगा। यानी यह ऑब्जेक्ट से स्थानांतरित करने के लिए सॉफ़्टवेयर के अन्य भागों को अनुमति देता है , लेकिन इसे स्थानांतरित करने की आवश्यकता नहीं है। वास्तव में, एक प्रतिद्वंद्वियों के संदर्भ के प्राप्तकर्ता को इस बारे में कोई वादा नहीं करना पड़ता है कि वह डेटा के साथ क्या करेगा या नहीं करेगा।
एरॉन मैकडैड

240

1. "यह क्या है?"

जबकि std::move() तकनीकी रूप से एक फ़ंक्शन है - मैं कहूंगा कि यह वास्तव में एक फ़ंक्शन नहीं है । यह एक तरह का है कनवर्टर तरीके के बीच संकलक एक अभिव्यक्ति के मूल्य को समझता है।

2. "यह क्या करता है?"

ध्यान देने वाली पहली बात यह है कि std::move() वास्तव में कुछ भी स्थानांतरित नहीं होता है । यह एक अभिव्यक्ति को एक लवल्यू (जैसे नामांकित चर) से एक ज्वलंत होने के रूप में परिवर्तित करता है । एक xvalue संकलक को बताता है:

आप मुझे लूट सकते हैं, मैं जो कुछ भी पकड़ रहा हूं उसे कहीं और स्थानांतरित कर सकता हूं (चूंकि मैं जल्द ही नष्ट होने वाला हूं)।

दूसरे शब्दों में, जब आप उपयोग करते हैं std::move(x), तो आप संकलक को नरभक्षण की अनुमति दे रहे हैं x। इस प्रकार यदि xस्मृति में इसका अपना बफर है, तो कहिए - std::move()संकलक के बाद संकलक के बदले कोई अन्य वस्तु हो सकती है।

आप एक प्रचलन से भी आगे बढ़ सकते हैं (जैसे कि एक अस्थायी जो आप चारों ओर से गुजर रहे हैं), लेकिन यह शायद ही कभी उपयोगी होता है।

3. "इसका उपयोग कब किया जाना चाहिए?"

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

एक विशिष्ट उपयोग संसाधनों को कॉपी करने के बजाय एक वस्तु से दूसरी वस्तु तक ले जाना है। @ गिलुयूम इस पृष्ठ से जुड़ता है जिसका एक सीधा छोटा उदाहरण है: कम नकल वाले दो वस्तुओं की अदला- बदली ।

template <class T>
swap(T& a, T& b) {
    T tmp(a);   // we now have two copies of a
    a = b;      // we now have two copies of b (+ discarded a copy of a)
    b = tmp;    // we now have two copies of tmp (+ discarded a copy of b)
}

चाल का उपयोग करने से आप संसाधनों को चारों ओर नकल करने के बजाय स्वैप कर सकते हैं:

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

सोचें कि क्या होता है जब T, कहते हैं, vector<int>आकार एन। पहले संस्करण में आप 3 * n तत्वों को पढ़ते हैं और लिखते हैं, दूसरे संस्करण में आप मूल रूप से वैक्टर के बफ़र्स को केवल 3 पॉइंटर्स पढ़ते हैं और लिखते हैं, साथ ही 3 बफ़र्स आकार। बेशक, कक्षा Tको यह जानने की ज़रूरत है कि चलती कैसे करें; आपकी कक्षा में कार्य करने के लिए एक चाल-असाइनमेंट ऑपरेटर और कक्षा के Tलिए एक चाल-निर्माणकर्ता होना चाहिए ।


3
लंबे समय से मैंने इन चाल शब्दार्थों के बारे में सुना है, मैंने कभी इन पर ध्यान नहीं दिया। आपके द्वारा दिए गए इस विवरण से ऐसा लगता है जैसे यह एक गहरी प्रति के बजाय उथली प्रति है।
जेब्राफिश

7
@TitoneMaurice: सिवाय इसके कि यह एक प्रति नहीं है - क्योंकि मूल मूल्य अब उपयोग करने योग्य नहीं है।
einpoklum

3
@Zebrafish आप अधिक गलत नहीं हो सकते। उथली प्रतिलिपि मूल को उसी स्थिति में छोड़ देती है, एक चाल के परिणामस्वरूप आमतौर पर मूल खाली या अन्यथा मान्य स्थिति में होता है।
रुबेंवब

16
@rubenvb ज़ेबरा पूरी तरह से गलत नहीं है। हालांकि यह सच है कि मूल कैनाबिलिज्ड ऑब्जेक्ट आम तौर पर भ्रमित करने वाली त्रुटियों से बचने के लिए जानबूझकर तोड़फोड़ की जाती है (उदाहरण के लिए इसके संकेत को इंगित करने के लिए नल्ट्र्र पर सेट करें कि यह अब पॉइंटर का मालिक नहीं है), यह तथ्य कि स्रोत से एक पॉइंटर को कॉपी करने के लिए पूरी चाल को लागू किया गया है गंतव्य के लिए (और जानबूझकर कुछ भी करने से बचना चाहिए) वास्तव में, मैं अब तक जाना होगा के रूप में कहना है कि एक कदम है एक उथले प्रतिलिपि, स्रोत के एक आंशिक आत्म विनाश से वैकल्पिक रूप से पालन किया। (प्रतियोगिता।)
को ऑर्बिट

3
(cont।) यदि हम इस परिभाषा (और मुझे पसंद है) की अनुमति देते हैं, तो @ ज़ेब्राफ़िश का अवलोकन गलत नहीं है, बस थोड़ा अधूरा है।
ऑर्बिट

145

जब आप किसी वस्तु की सामग्री को कहीं और "ट्रांसफर" करने की आवश्यकता हो तो आप इसका उपयोग कर सकते हैं, बिना कॉपी किए (यानी सामग्री को डुप्लिकेट नहीं किया जाता है, इसीलिए इसका उपयोग कुछ गैर-प्रतिलिपि योग्य वस्तुओं पर किया जा सकता है, जैसे कि unique_ptr)। किसी वस्तु के लिए यह भी संभव है कि वह एक अस्थायी वस्तु की सामग्री को कॉपी किए बिना (और बहुत समय बचाए), std :: चाल के साथ ले जाए।

इस लिंक ने वास्तव में मेरी मदद की:

http://thbecker.net/articles/rvalue_references/section_01.html

मुझे खेद है कि अगर मेरा उत्तर बहुत देर से आ रहा है, लेकिन मैं भी std :: कदम के लिए एक अच्छी लिंक की तलाश कर रहा था, और मुझे थोड़ा "austere" लिंक मिला।

यह आर-मूल्य संदर्भ पर जोर डालता है, आपको उन्हें किस संदर्भ में उपयोग करना चाहिए, और मुझे लगता है कि यह अधिक विस्तृत है, इसलिए मैं इस लिंक को यहां साझा करना चाहता था।


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

66

प्रश्न: क्या है std::move?

एक: std::move()सी + + मानक पुस्तकालय से एक समारोह के लिए एक संदर्भ के लिए कास्टिंग है।

सरलीकृत रूप std::move(t)से इसके बराबर है:

static_cast<T&&>(t);

एक प्रतिद्वंद्विता एक अस्थायी है जो इसे परिभाषित करने वाली अभिव्यक्ति से परे नहीं रहती है, जैसे कि एक मध्यवर्ती फ़ंक्शन परिणाम जो एक चर में कभी संग्रहीत नहीं होता है।

int a = 3; // 3 is a rvalue, does not exist after expression is evaluated
int b = a; // a is a lvalue, keeps existing after expression is evaluated

St20 :: move () के लिए एक कार्यान्वयन N2027 में दिया गया है : "ए रेवल्यूशन टू रिवल्यू रेफरेंस" इस प्रकार है:

template <class T>
typename remove_reference<T>::type&&
std::move(T&& a)
{
    return a;
}

आप देख सकते हैं, std::moveरिटर्न T&&कोई फर्क नहीं पड़ता अगर एक मूल्य (के साथ बुलाया T), संदर्भ प्रकार ( T&) या rvalue संदर्भ ( T&&)।

प्रश्न: यह क्या करता है?

एक: एक कलाकार के रूप में, यह रनटाइम के दौरान कुछ भी नहीं करता है। संकलक को यह बताने के लिए संकलन समय पर प्रासंगिक है कि आप संदर्भ के रूप में विचार करना जारी रखना चाहेंगे।

foo(3 * 5); // obviously, you are calling foo with a temporary (rvalue)

int a = 3 * 5;
foo(a);     // how to tell the compiler to treat `a` as an rvalue?
foo(std::move(a)); // will call `foo(int&& a)` rather than `foo(int a)` or `foo(int& a)`

यह क्या नहीं करता है:

  • तर्क की एक प्रति बनाएँ
  • कॉपी कंस्ट्रक्टर को बुलाओ
  • तर्क वस्तु बदलें

प्रश्न: इसका इस्तेमाल कब किया जाना चाहिए?

ए: आपको उपयोग करना चाहिए std::moveयदि आप ऐसे कार्यों को कॉल करना चाहते हैं जो एक शब्दार्थ के साथ गतिविरोधों का समर्थन करते हैं जो एक प्रतिद्वंद्विता (अस्थायी अभिव्यक्ति) नहीं है।

यह मेरे लिए निम्नलिखित फॉलो-अप प्रश्न पूछता है:

  • चाल शब्दार्थ क्या हैं? कॉपी शब्दार्थों के विपरीत शब्दार्थ को स्थानांतरित करना एक प्रोग्रामिंग तकनीक है जिसमें किसी वस्तु के सदस्यों को किसी अन्य वस्तु के सदस्यों को कॉपी करने के बजाय 'टेक ओवर' द्वारा आरम्भ किया जाता है। इस तरह का 'टेक ओवर' केवल पॉइंटर्स और रिसोर्स हैंडल से ही समझ में आता है, जिसे अंतर्निहित डेटा के बजाय पॉइंटर या पूर्णांक हैंडल को कॉपी करके सस्ते में ट्रांसफर किया जा सकता है।

  • किस प्रकार की कक्षाएं और ऑब्जेक्ट्स शब्दार्थ को स्थानांतरित करते हैं? यह आप पर निर्भर है कि यदि आप अपने सदस्यों को कॉपी करने के बजाय उनके सदस्यों को स्थानांतरित करने से लाभान्वित होंगे, तो अपनी कक्षाओं में चालित शब्दार्थ को लागू करने के लिए एक डेवलपर के रूप में। एक बार जब आप चाल शब्दार्थों को लागू करते हैं, तो आपको कई पुस्तकालय प्रोग्रामर से सीधे काम का फायदा होगा, जिन्होंने चालित शब्दार्थ के साथ कक्षाओं को कुशलता से संभालने के लिए समर्थन जोड़ा है।

  • संकलक इसे अपने आप क्यों नहीं समझ सकता है? कंपाइलर किसी फ़ंक्शन के दूसरे अधिभार को तब तक कॉल नहीं कर सकता जब तक आप ऐसा न कहें। आपको कंपाइलर को यह चुनने में मदद करनी चाहिए कि फ़ंक्शन के नियमित या चाल संस्करण को बुलाया जाना चाहिए या नहीं।

  • मैं किन स्थितियों में संकलक को बताना चाहूंगा कि उसे एक चर के रूप में व्यवहार करना चाहिए? यह खाका या पुस्तकालय कार्यों में सबसे अधिक संभावना होगी, जहां आप जानते हैं कि एक मध्यवर्ती परिणाम को बचाया जा सकता है।


2
टिप्पणियों में शब्दार्थ के साथ कोड उदाहरणों के लिए बिग +1। अन्य शीर्ष उत्तर std :: "चाल" का उपयोग कर स्वयं को परिभाषित करते हैं - वास्तव में कुछ भी स्पष्ट नहीं करता है! --- मेरा मानना ​​है कि यह ध्यान देने योग्य है कि तर्क की एक प्रति न बनाने का मतलब है कि मूल मूल्य का मज़बूती से उपयोग नहीं किया जा सकता है।
ty

34

std :: चाल ही वास्तव में बहुत कुछ नहीं करता है। मैंने सोचा था कि यह एक वस्तु के लिए स्थानांतरित कंस्ट्रक्टर को बुलाता है, लेकिन यह वास्तव में सिर्फ एक प्रकार कास्ट (एक लवल्यू चर को एक रवेल्यू में कास्टिंग करता है ताकि उक्त वेरिएबल को एक मूव कंस्ट्रक्टर या असाइनमेंट ऑपरेटर के तर्क के रूप में पारित किया जा सके)।

तो std :: चाल का उपयोग केवल चाल शब्दार्थ का उपयोग करने के लिए अग्रदूत के रूप में किया जाता है। अस्थायी वस्तुओं से निपटने के लिए मूव शब्दार्थ अनिवार्य रूप से एक कुशल तरीका है।

वस्तु पर विचार करें A = B + C + D + E + F;

यह अच्छा दिखने वाला कोड है, लेकिन E + F एक अस्थायी ऑब्जेक्ट बनाता है। फिर D + अस्थायी एक और अस्थायी वस्तु का उत्पादन करता है। प्रत्येक सामान्य "+" वर्ग के ऑपरेटर में, गहरी प्रतियां होती हैं।

उदाहरण के लिए

Object Object::operator+ (const Object& rhs) {
    Object temp (*this);
    // logic for adding
    return temp;
}

इस फ़ंक्शन में अस्थायी ऑब्जेक्ट का निर्माण बेकार है - इन अस्थायी वस्तुओं को लाइन के अंत में वैसे भी हटा दिया जाएगा क्योंकि वे दायरे से बाहर जाते हैं।

हम बल्कि अस्थायी वस्तुओं को "लूट" करने के लिए चालित शब्दार्थ का उपयोग कर सकते हैं और कुछ ऐसा कर सकते हैं

 Object& Object::operator+ (Object&& rhs) {
     // logic to modify rhs directly
     return rhs;
 }

इससे अनावश्यक गहरी प्रतियां बनाई जा रही हैं। उदाहरण के संदर्भ में, एकमात्र भाग जहां गहरी नकल होती है, वह अब E + F है। बाकी चालित शब्दार्थ का उपयोग करता है। परिणाम ए को असाइन करने के लिए मूव कंस्ट्रक्टर या असाइनमेंट ऑपरेटर को भी लागू करना होगा।


3
आपने चाल शब्दार्थ के बारे में बताया। आपको अपने उत्तर में यह जोड़ना चाहिए कि कैसे std :: Move का उपयोग किया जा सकता है क्योंकि प्रश्न उस बारे में पूछता है।
कौशिक शेट्टी

2
@ कौशिक std :: चाल doesnt ज्यादा - लेकिन चाल शब्दार्थ को लागू करने के लिए प्रयोग किया जाता है। यदि आप std :: चाल के बारे में नहीं जानते हैं, तो आप शायद या तो शब्दार्थ को नहीं जानते हैं
user929404

1
"बहुत कुछ नहीं करता है" (हाँ सिर्फ एक static_cast से एक रेवल्यू संदर्भ के लिए)। वास्तव में यह क्या करता है और y यह करता है जो ओपी ने पूछा है। आपको यह नहीं पता होना चाहिए कि std :: Move कैसे काम करता है, लेकिन आपको पता चल गया है कि शब्दार्थ क्या करता है। इसके अलावा, "लेकिन कदम अर्थ को लागू करने के लिए प्रयोग किया जाता है" इसके चारों ओर मार्ग। मूव शब्दार्थ को जानिए और समझ लीजिए std :: Move अन्यथा नहीं। चाल बस आंदोलन में मदद करता है और खुद चाल शब्दार्थ का उपयोग करता है। std :: Move कुछ नहीं करता है लेकिन अपने तर्क को संदर्भ के संदर्भ में बदल देता है, जो कि शब्दार्थ को स्थानांतरित करने की आवश्यकता है।
कौशिक शेट्टी

10
"लेकिन ई + एफ एक अस्थायी वस्तु का उत्पादन करता है" - ऑपरेटर +बाएं से दाएं जाता है, दाएं से बाएं नहीं। इसलिए B+Cपहले होगा!
अजय

8

"यह क्या है?" और "यह क्या करता है?" ऊपर बताया गया है।

मैं "जब इसका उपयोग किया जाना चाहिए" का एक उदाहरण दूंगा

उदाहरण के लिए, हमारे पास एक वर्ग है जिसमें बहुत सारे संसाधन हैं जैसे बड़े सरणी।

class ResHeavy{ //  ResHeavy means heavy resource
    public:
        ResHeavy(int len=10):_upInt(new int[len]),_len(len){
            cout<<"default ctor"<<endl;
        }

        ResHeavy(const ResHeavy& rhs):_upInt(new int[rhs._len]),_len(rhs._len){
            cout<<"copy ctor"<<endl;
        }

        ResHeavy& operator=(const ResHeavy& rhs){
            _upInt.reset(new int[rhs._len]);
            _len = rhs._len;
            cout<<"operator= ctor"<<endl;
        }

        ResHeavy(ResHeavy&& rhs){
            _upInt = std::move(rhs._upInt);
            _len = rhs._len;
            rhs._len = 0;
            cout<<"move ctor"<<endl;
        }

    // check array valid
    bool is_up_valid(){
        return _upInt != nullptr;
    }

    private:
        std::unique_ptr<int[]> _upInt; // heavy array resource
        int _len; // length of int array
};

टेस्ट कोड:

void test_std_move2(){
    ResHeavy rh; // only one int[]
    // operator rh

    // after some operator of rh, it becomes no-use
    // transform it to other object
    ResHeavy rh2 = std::move(rh); // rh becomes invalid

    // show rh, rh2 it valid
    if(rh.is_up_valid())
        cout<<"rh valid"<<endl;
    else
        cout<<"rh invalid"<<endl;

    if(rh2.is_up_valid())
        cout<<"rh2 valid"<<endl;
    else
        cout<<"rh2 invalid"<<endl;

    // new ResHeavy object, created by copy ctor
    ResHeavy rh3(rh2);  // two copy of int[]

    if(rh3.is_up_valid())
        cout<<"rh3 valid"<<endl;
    else
        cout<<"rh3 invalid"<<endl;
}

नीचे के रूप में उत्पादन:

default ctor
move ctor
rh invalid
rh2 valid
copy ctor
rh3 valid

हम देख सकते हैं कि std::moveके साथ move constructorबनाता है आसानी से संसाधन को बदलने।

और कहाँ std::moveउपयोगी है?

std::moveतत्वों की एक सरणी को सॉर्ट करते समय भी उपयोगी हो सकता है। तत्वों की जोड़ी को स्वैप करके कई सॉर्टिंग एल्गोरिदम (जैसे चयन सॉर्ट और बबल सॉर्ट) काम करते हैं। पहले, हमें स्वैपिंग करने के लिए कॉपी-शब्दार्थ का सहारा लेना पड़ता था। अब हम चाल शब्दार्थ का उपयोग कर सकते हैं, जो अधिक कुशल है।

यह भी उपयोगी हो सकता है अगर हम एक स्मार्ट पॉइंटर द्वारा प्रबंधित सामग्री को दूसरे में स्थानांतरित करना चाहते हैं।

उद्धृत:


0

यहाँ एक पूर्ण उदाहरण है, std :: a (सरल) कस्टम वेक्टर के लिए कदम

अपेक्षित उत्पादन:

 c: [10][11]
 copy ctor called
 copy of c: [10][11]
 move ctor called
 moved c: [10][11]

संकलन इस प्रकार है:

  g++ -std=c++2a -O2 -Wall -pedantic foo.cpp

कोड:

#include <iostream>
#include <algorithm>

template<class T> class MyVector {
private:
    T *data;
    size_t maxlen;
    size_t currlen;
public:
    MyVector<T> () : data (nullptr), maxlen(0), currlen(0) { }
    MyVector<T> (int maxlen) : data (new T [maxlen]), maxlen(maxlen), currlen(0) { }

    MyVector<T> (const MyVector& o) {
        std::cout << "copy ctor called" << std::endl;
        data = new T [o.maxlen];
        maxlen = o.maxlen;
        currlen = o.currlen;
        std::copy(o.data, o.data + o.maxlen, data);
    }

    MyVector<T> (const MyVector<T>&& o) {
        std::cout << "move ctor called" << std::endl;
        data = o.data;
        maxlen = o.maxlen;
        currlen = o.currlen;
    }

    void push_back (const T& i) {
        if (currlen >= maxlen) {
            maxlen *= 2;
            auto newdata = new T [maxlen];
            std::copy(data, data + currlen, newdata);
            if (data) {
                delete[] data;
            }
            data = newdata;
        }
        data[currlen++] = i;
    }

    friend std::ostream& operator<<(std::ostream &os, const MyVector<T>& o) {
        auto s = o.data;
        auto e = o.data + o.currlen;;
        while (s < e) {
            os << "[" << *s << "]";
            s++;
        }
        return os;
    }
};

int main() {
    auto c = new MyVector<int>(1);
    c->push_back(10);
    c->push_back(11);
    std::cout << "c: " << *c << std::endl;
    auto d = *c;
    std::cout << "copy of c: " << d << std::endl;
    auto e = std::move(*c);
    delete c;
    std::cout << "moved c: " << e << std::endl;
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.