कैसे पीछा करते हुए नकल करने के लिए?


10

मैं नीचे छोटे उदाहरण जैसे चेनिंग-प्रकार का एक वर्ग बना रहा हूं। ऐसा लगता है कि जब सदस्य काम करते हैं, तो कॉपी कंस्ट्रक्टर को आमंत्रित किया जाता है। क्या कॉपी कंस्ट्रक्टर कॉल से छुटकारा पाने का एक तरीका है? नीचे मेरे खिलौने के उदाहरण में, यह स्पष्ट है कि मैं केवल अस्थायी लोगों के साथ काम कर रहा हूं और इस तरह "" होना चाहिए (शायद मानकों द्वारा नहीं, लेकिन तार्किक रूप से) एक चीरा हो। दूसरी सबसे अच्छी पसंद, एलिसन को कॉपी करने के लिए, कॉल कंस्ट्रक्टर को कॉल करने के लिए होगा, लेकिन ऐसा नहीं है।

class test_class {
    private:
    int i = 5;
    public:
    test_class(int i) : i(i) {}
    test_class(const test_class& t) {
        i = t.i;
        std::cout << "Copy constructor"<< std::endl;
    }
    test_class(test_class&& t) {
        i = t.i;
        std::cout << "Move constructor"<< std::endl;
    }
    auto& increment(){
        i++;
        return *this;
    }
};
int main()
{
    //test_class a{7};
    //does not call copy constructor
    auto b = test_class{7};
    //calls copy constructor
    auto b2 = test_class{7}.increment();
    return 0;
}

संपादित करें: कुछ स्पष्टीकरण। 1. यह अनुकूलन स्तर पर निर्भर नहीं करता है। 2. मेरे वास्तविक कोड में, मेरे पास अधिक जटिल (जैसे ढेर आवंटित) वस्तुएं हैं


संकलन के लिए आप किस अनुकूलन स्तर का उपयोग करते हैं?
JVApen

2
auto b = test_class{7};कॉपी कंस्ट्रक्टर को कॉल नहीं करता है क्योंकि यह वास्तव में बराबर है test_class b{7};और कंपाइलर इस मामले को पहचानने के लिए पर्याप्त स्मार्ट हैं और इसलिए आसानी से किसी भी नकल को खत्म कर सकते हैं। उसी के लिए नहीं किया जा सकता है b2
कुछ प्रोग्रामर ने

दिखाए गए उदाहरण में, चाल और प्रतिलिपि के बीच वास्तविक या कोई अंतर नहीं हो सकता है और हर कोई इसके बारे में नहीं जानता है। यदि आप एक बड़े वेक्टर की तरह कुछ अटक जाते हैं तो यह एक अलग मामला हो सकता है। आम तौर पर चाल केवल प्रकारों का उपयोग करके संसाधन के लिए समझ में आता है (जैसे ढेर ढेर का उपयोग करके मेम।, आदि) - क्या यहां मामला है?
सुबह

उदाहरण से वंचित दिखता है। क्या आपके पास वास्तव में I / O ( std::cout) आपकी कॉपी ctor में है? इसके बिना प्रतिलिपि को दूर अनुकूलित किया जाना चाहिए।
rustyx

@rustyx, std :: cout निकालें और कॉपी कंस्ट्रक्टर को स्पष्ट करें। यह दर्शाता है कि कॉपी एलिसन std :: cout निर्भर नहीं है।
डीडीएनएल

जवाबों:


7
  1. आंशिक उत्तर (यह b2जगह में निर्माण नहीं करता है, लेकिन प्रतिलिपि निर्माण को एक चाल निर्माण में बदल देता है): आप incrementसंबंधित उदाहरण के मूल्य श्रेणी पर सदस्य फ़ंक्शन को अधिभारित कर सकते हैं :

    auto& increment() & {
        i++;
        return *this;
    }
    
    auto&& increment() && {
        i++;
       return std::move(*this);
    }

    इसकी वजह से

    auto b2 = test_class{7}.increment();

    स्थानांतरित करने के लिए निर्माण b2क्योंकि test_class{7}एक अस्थायी है, और का &&अधिभार test_class::incrementकहा जाता है।

  2. एक सच्चे इन-प्लेस निर्माण (यानी एक निर्माण भी नहीं) के लिए, आप सभी विशेष और गैर-विशेष सदस्य फ़ंक्शंस को constexprसंस्करणों में बदल सकते हैं। फिर, आप कर सकते हैं

    constexpr auto b2 = test_class{7}.increment();

    और आप भुगतान करने के लिए न तो एक कदम और न ही एक प्रतिलिपि निर्माण। यह, जाहिर है, सरल के लिए संभव है test_class, लेकिन अधिक सामान्य परिदृश्य के लिए नहीं जो constexprसदस्य कार्यों के लिए अनुमति नहीं देता है ।


दूसरा विकल्प b2गैर-परिवर्तनीय भी बनाता है ।
कुछ प्रोग्रामर 10

1
@Someprogrammerdude अच्छा बिंदु। मुझे लगता है कि constinitवहाँ जाने का रास्ता होगा।
lubgr

फ़ंक्शन नाम के बाद एम्परसेंड का उद्देश्य क्या है? मैंने ऐसा पहले कभी नहीं देखा।
फिलिप नेल्सन

1
@PhilipNelson वे रेफ-क्वालीफायर हैं और यह निर्धारित कर सकते हैं कि सदस्य कार्यों thisके समान ही क्या होconst
kmdreko

1

मूल रूप से, असाइन करना a एक को करने के लिए एक निर्माता, यानी एक प्रतिलिपि या एक चाल को लागू करने की आवश्यकता होती है । यह से अलग है जहां यह फ़ंक्शन के दोनों किनारों पर समान विशिष्ट वस्तु के रूप में जाना जाता है। एक भी एक पॉइंटर की तरह एक साझा वस्तु को कर सकता है।


सबसे सरल तरीका संभवतः कॉपी कंस्ट्रक्टर को पूरी तरह से अनुकूलित किया जाना है। मान सेटिंग पहले से ही कंपाइलर द्वारा ऑप्टिमाइज़ किया गया है, यह सिर्फ इतना है std::coutकि इसे ऑप्टिमाइज़ नहीं किया जा सकता है।

test_class(const test_class& t) = default;

(या बस कॉपी और मूव कंस्ट्रक्टर दोनों को हटा दें)

जीवंत उदाहरण


चूँकि आपका मुद्दा मूल रूप से संदर्भ के साथ है, एक समाधान संभवतः ऑब्जेक्ट का संदर्भ नहीं दे रहा है यदि आप इस तरह से नकल रोकना चाहते हैं।

  void increment();
};

auto b = test_class{7};//does not call copy constructor
b.increment();//does not call copy constructor

एक तीसरी विधि सिर्फ पहली जगह कॉपी एलक्शन पर निर्भर है - हालांकि इसके लिए एक फ़ंक्शन में ऑपरेशन को फिर से लिखना या इनकैप्सुलेशन की आवश्यकता होती है और इस तरह मुद्दे से पूरी तरह से बचना (मुझे पता है कि यह वह नहीं है जो आप चाहते हैं, लेकिन एक हो सकता है अन्य उपयोगकर्ताओं के लिए समाधान):

auto b2 = []{test_class tmp{7}; tmp.increment().increment().increment(); return tmp;}(); //<-- b2 becomes 10 - copy constructor not called

एक चौथा तरीका इसके बजाय एक चाल का उपयोग कर रहा है, या तो स्पष्ट रूप से लागू किया गया है

auto b2 = std::move(test_class{7}.increment());

या जैसा देखा गया इस उत्तर


@ ओ'नील एक और मामले के बारे में सोच रहा था - ty, सुधारा
darune
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.