कॉपी एलीशन और रिटर्न वैल्यू ऑप्टिमाइज़ेशन क्या हैं?


377

कॉपी एलीशन क्या है? (नामित) रिटर्न वैल्यू ऑप्टिमाइज़ेशन क्या है? वे क्या मतलब है?

वे किन स्थितियों में हो सकते हैं? सीमाएं क्या हैं?


1
कॉपी एलिसन इसे देखने का एक तरीका है; ऑब्जेक्ट एलीगेशन या ऑब्जेक्ट फ्यूजन (या भ्रम) एक और दृश्य है।
23

मुझे यह लिंक मददगार लगी।
सबलेसेकेर

जवाबों:


246

परिचय

एक तकनीकी अवलोकन के लिए - इस उत्तर पर जाएं

सामान्य मामलों के लिए जहां कॉपी एलिज़न होता है - इस उत्तर पर जाएं

कुछ स्थितियों में अतिरिक्त (संभावित रूप से महंगी) प्रतियों को रोकने के लिए प्रतिलिपि संविलियन अधिकांश संकलक द्वारा कार्यान्वित एक अनुकूलन है। यह मूल्य या पास-बाय-वैल्यू व्यवहार में संभव होता है (प्रतिबंध लागू होता है)।

यह अनुकूलन का एकमात्र रूप है जो el-ha (!) As-if नियम - कॉपी एलिजन लागू किया जा सकता है भले ही ऑब्जेक्ट को कॉपी / मूव करने के साइड-इफेक्ट हों

विकिपीडिया से लिया गया निम्न उदाहरण :

struct C {
  C() {}
  C(const C&) { std::cout << "A copy was made.\n"; }
};

C f() {
  return C();
}

int main() {
  std::cout << "Hello World!\n";
  C obj = f();
}

संकलक और सेटिंग्स के आधार पर, निम्नलिखित आउटपुट सभी मान्य हैं :

नमस्ते दुनिया!
एक प्रति बनाई गई थी।
एक प्रति बनाई गई थी।


नमस्ते दुनिया!
एक प्रति बनाई गई थी।


नमस्ते दुनिया!

इसका मतलब यह भी है कि कम वस्तुओं का निर्माण किया जा सकता है, इसलिए आप विशिष्ट संख्या में विध्वंसक भी कहे जा सकते हैं। आपको कॉपी / मूव-कंस्ट्रक्टर या डिस्ट्रक्टर्स के अंदर महत्वपूर्ण तर्क नहीं देना चाहिए, क्योंकि आप उन्हें कॉल किए जाने पर भरोसा नहीं कर सकते।

अगर एक कॉपी या मूव कंस्ट्रक्टर को कॉल किया जाता है, तो वह कंस्ट्रक्टर अभी भी मौजूद होना चाहिए और एक्सेस होना चाहिए। यह सुनिश्चित करता है कि कॉपी एलीशन उन वस्तुओं को कॉपी करने की अनुमति नहीं देता है जो सामान्य रूप से कॉपी करने योग्य नहीं हैं, उदाहरण के लिए, क्योंकि उनके पास एक निजी या हटाए गए कॉपी / मूव कंस्ट्रक्टर हैं।

C ++ 17 : C ++ 17 के रूप में, किसी वस्तु को सीधे लौटाए जाने पर Copy Elision की गारंटी दी जाती है:

struct C {
  C() {}
  C(const C&) { std::cout << "A copy was made.\n"; }
};

C f() {
  return C(); //Definitely performs copy elision
}
C g() {
    C c;
    return c; //Maybe performs copy elision
}

int main() {
  std::cout << "Hello World!\n";
  C obj = f(); //Copy constructor isn't called
}

2
क्या आप बता सकते हैं कि 2nd आउटपुट कब होता है और 3rd कब होता है?
झंगक्साओचेन

3
@zhangxaochen संकलक कब और कैसे इस तरह से अनुकूलन करने का निर्णय लेता है।
ल्यूचियन ग्रिगोर

10
@ ज़्न्न्गाक्सैचेन, 1 आउटपुट: कॉपी 1 एक टेम्प पर लौटने से है, और 2 से कॉपी करने के लिए टेम्प से obj; 2 है जब उपरोक्त में से एक को चुना जाता है, तो शायद प्रतिरूप की प्रतिलिपि बनाई गई है; थ्रिस दोनों अभ्यस्त हैं
विजेता

2
हम्म, लेकिन मेरी राय में, यह जरूरी एक ऐसी विशेषता है जिस पर हम भरोसा कर सकते हैं। क्योंकि अगर हम नहीं कर सकते हैं, तो यह आधुनिक C ++ (RVO बनाम std :: move) में हमारे कार्यों को लागू करने के तरीके को बुरी तरह प्रभावित करेगा। CppCon 2014 के कुछ वीडियो देखने के दौरान, मुझे वास्तव में आभास हुआ कि सभी आधुनिक कंपाइलर हमेशा आरवीओ करते हैं। इसके अलावा, मैंने कहीं पढ़ा है कि किसी भी अनुकूलन के बिना, संकलक इसे लागू करते हैं। लेकिन, निश्चित रूप से, मैं इसके बारे में निश्चित नहीं हूं। इसलिए मैं पूछ रहा हूं।
j00hi

8
@ j00hi: रिटर्न स्टेटमेंट में कभी भी मूव न लिखें - यदि rvo लागू नहीं है, तो रिटर्न वैल्यू किसी भी तरह डिफ़ॉल्ट रूप से बाहर ले जाया जाता है।
माइक एमबी

96

मानक संदर्भ

कम तकनीकी दृश्य और परिचय के लिए - इस उत्तर पर जाएं

सामान्य मामलों के लिए जहां कॉपी एलिज़न होता है - इस उत्तर पर जाएं

कॉपी एलिज़न को मानक में परिभाषित किया गया है:

12.8 कोडिंग और चलती हुई क्लास ऑब्जेक्ट [class.copy]

जैसा

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

- क्लास रिटर्न प्रकार के साथ एक फ़ंक्शन में रिटर्न स्टेटमेंट में, जब अभिव्यक्ति एक गैर-वाष्पशील स्वचालित ऑब्जेक्ट (फ़ंक्शन या कैच-क्लॉज पैरामीटर के अलावा) का नाम होता है, जो फ़ंक्शन रिटर्न प्रकार के समान cvunqualified प्रकार के साथ होता है, कॉपी / मूव ऑपरेशन को ऑटोमैटिक ऑब्जेक्ट को सीधे फंक्शन के रिटर्न वैल्यू में बनाकर छोड़ा जा सकता है

- थ्रो-एक्सप्रेशन में, जब ऑपरेंड एक गैर-वाष्पशील स्वचालित वस्तु (एक फ़ंक्शन या कैच-क्लॉज पैरामीटर के अलावा) का नाम है, जिसका दायरा कोशिश-ब्लॉक को संलग्न करने वाले अंतरतम के अंत से आगे नहीं बढ़ता है (यदि वहां है) एक), ऑपरेंड से अपवाद ऑब्जेक्ट (15.1) पर कॉपी / मूव ऑपरेशन को ऑटोमैटिक ऑब्जेक्ट को सीधे अपवाद ऑब्जेक्ट में बनाकर छोड़ा जा सकता है

- जब एक अस्थायी वर्ग ऑब्जेक्ट जो एक संदर्भ (12.2) के लिए बाध्य नहीं किया गया है, तो उसी cv-अयोग्य प्रकार के साथ एक क्लास ऑब्जेक्ट को कॉपी / स्थानांतरित किया जाएगा, तो अस्थायी ऑब्जेक्ट को सीधे निर्माण करके कॉपी / मूव ऑपरेशन को छोड़ा जा सकता है छोड़ी गई प्रतिलिपि / चाल का लक्ष्य

- जब अपवाद हैंडलर (क्लाज 15) के अपवाद-घोषणा को ऑब्जेक्ट (15.1) के रूप में एक ही प्रकार की वस्तु (सीवी-योग्यता को छोड़कर) घोषित करता है, तो कॉपी-मूव ऑपरेशन को अपवाद-घोषणा का इलाज करके छोड़ा जा सकता है अपवाद वस्तु के लिए एक उपनाम के रूप में अगर कार्यक्रम का अर्थ अपवाद-घोषणा द्वारा घोषित वस्तु के लिए निर्माणकर्ताओं और विध्वंसक के निष्पादन को छोड़कर अपरिवर्तित होगा।

123) क्योंकि दो के बजाय केवल एक ही वस्तु नष्ट होती है, और एक कॉपी / मूव कंस्ट्रक्टर को निष्पादित नहीं किया जाता है, फिर भी हर एक निर्माण के लिए एक वस्तु नष्ट हो जाती है।

दिया गया उदाहरण है:

class Thing {
public:
  Thing();
  ~Thing();
  Thing(const Thing&);
};
Thing f() {
  Thing t;
  return t;
}
Thing t2 = f();

और स्पष्टीकरण:

यहां क्लास के कॉपी कंस्ट्रक्टर को दो कॉल को खत्म करने के लिए एलिज़न के मानदंड को जोड़ा जा सकता है Thing: tफ़ंक्शन के रिटर्न वैल्यू के लिए अस्थायी ऑब्जेक्ट में स्थानीय स्वचालित ऑब्जेक्ट की कॉपी और ऑब्जेक्ट में उस अस्थायी ऑब्जेक्ट f() की कॉपी t2। प्रभावी रूप से, स्थानीय वस्तु के निर्माण t को सीधे वैश्विक वस्तु को आरम्भ करने के रूप में देखा जा सकता है t2, और उस वस्तु का विनाश कार्यक्रम से बाहर निकलने पर होगा। एक मूविंग कंस्ट्रक्टर को थिंग में जोड़ने का एक ही प्रभाव है, लेकिन यह अस्थायी ऑब्जेक्ट से स्थानांतरित करने के लिए निर्माण t2है।


1
क्या यह C ++ 17 मानक से या पहले के संस्करण से है?
निल्स

90

नकल के सामान्य रूप चीरा

एक तकनीकी अवलोकन के लिए - इस उत्तर पर जाएं

कम तकनीकी दृश्य और परिचय के लिए - इस उत्तर पर जाएं

(नामांकित) रिटर्न वैल्यू ऑप्टिमाइज़ेशन कॉपी एलिसन का एक सामान्य रूप है। यह उस स्थिति को संदर्भित करता है, जहां किसी विधि से मूल्य द्वारा लौटाए गए ऑब्जेक्ट में इसकी प्रतिलिपि लम्बी होती है। मानक में सामने दिया गया उदाहरण रिटर्न वैल्यू ऑप्टिमाइज़ेशन नाम देता है , क्योंकि ऑब्जेक्ट का नाम दिया गया है।

class Thing {
public:
  Thing();
  ~Thing();
  Thing(const Thing&);
};
Thing f() {
  Thing t;
  return t;
}
Thing t2 = f();

एक अस्थायी लौटाए जाने पर नियमित रिटर्न वैल्यू ऑप्टिमाइज़ेशन होता है:

class Thing {
public:
  Thing();
  ~Thing();
  Thing(const Thing&);
};
Thing f() {
  return Thing();
}
Thing t2 = f();

अन्य सामान्य स्थान जहां कॉपी एलिसेंस होता है, जब एक अस्थायी मूल्य द्वारा पारित किया जाता है :

class Thing {
public:
  Thing();
  ~Thing();
  Thing(const Thing&);
};
void foo(Thing t);

foo(Thing());

या जब एक अपवाद फेंक दिया जाता है और मूल्य द्वारा पकड़ा जाता है :

struct Thing{
  Thing();
  Thing(const Thing&);
};

void foo() {
  Thing c;
  throw c;
}

int main() {
  try {
    foo();
  }
  catch(Thing c) {  
  }             
}

कॉपी एलिसन की सामान्य सीमाएँ हैं:

  • कई वापसी अंक
  • सशर्त आरंभीकरण

अधिकांश व्यावसायिक-श्रेणी के कंपाइलर कॉपी एलिजन & (एन) आरवीओ (अनुकूलन सेटिंग्स के आधार पर) का समर्थन करते हैं।


4
मैं "कॉमन लिमिट्स" बुलेट पॉइंट्स को देखने में दिलचस्पी लेता हूँ, बस थोड़ा सा समझाया गया है ... क्या ये सीमित कारक हैं?
फोनटैगर

@phonetagger मैं msdn लेख के खिलाफ जुड़ा हुआ है, आशा है कि कुछ सामान को साफ करता है।
लुचियन ग्रिगोर

54

कॉपी एलिजन एक कंपाइलर ऑप्टिमाइज़ेशन तकनीक है जो अनावश्यक कॉपीिंग / ऑब्जेक्ट्स को हिलाने से दूर करती है।

निम्नलिखित परिस्थितियों में, एक कंपाइलर को कॉपी / मूव ऑपरेशन को छोड़ने की अनुमति दी जाती है और इसलिए संबंधित कंस्ट्रक्टर को कॉल नहीं करना चाहिए:

  1. NRVO (नामांकित वापसी मूल्य अनुकूलन) : यदि कोई फ़ंक्शन मान के द्वारा एक वर्ग प्रकार देता है और रिटर्न स्टेटमेंट की अभिव्यक्ति स्वचालित भंडारण अवधि (जो फ़ंक्शन पैरामीटर नहीं है) के साथ एक गैर-वाष्पशील वस्तु का नाम है, तो प्रतिलिपि / चाल कि एक गैर-अनुकूलन संकलक द्वारा किया जाएगा छोड़ा जा सकता है। यदि ऐसा है, तो लौटाया गया मान सीधे उस स्टोरेज में निर्मित होता है जिसमें फ़ंक्शन का रिटर्न मान अन्यथा स्थानांतरित या कॉपी किया जाएगा।
  2. आरवीओ (रिटर्न वैल्यू ऑप्टिमाइजेशन) : यदि फ़ंक्शन नामहीन अस्थायी ऑब्जेक्ट लौटाता है जिसे एक भोली संकलक द्वारा स्थानांतरित या गंतव्य में कॉपी किया जाता है, तो प्रतिलिपि या चाल को 1 के अनुसार छोड़ा जा सकता है।
#include <iostream>  
using namespace std;

class ABC  
{  
public:   
    const char *a;  
    ABC()  
     { cout<<"Constructor"<<endl; }  
    ABC(const char *ptr)  
     { cout<<"Constructor"<<endl; }  
    ABC(ABC  &obj)  
     { cout<<"copy constructor"<<endl;}  
    ABC(ABC&& obj)  
    { cout<<"Move constructor"<<endl; }  
    ~ABC()  
    { cout<<"Destructor"<<endl; }  
};

ABC fun123()  
{ ABC obj; return obj; }  

ABC xyz123()  
{  return ABC(); }  

int main()  
{  
    ABC abc;  
    ABC obj1(fun123());//NRVO  
    ABC obj2(xyz123());//NRVO  
    ABC xyz = "Stack Overflow";//RVO  
    return 0;  
}

**Output without -fno-elide-constructors**  
root@ajay-PC:/home/ajay/c++# ./a.out   
Constructor    
Constructor  
Constructor  
Constructor  
Destructor  
Destructor  
Destructor  
Destructor  

**Output with -fno-elide-constructors**  
root@ajay-PC:/home/ajay/c++# g++ -std=c++11 copy_elision.cpp -fno-elide-constructors    
root@ajay-PC:/home/ajay/c++# ./a.out   
Constructor  
Constructor  
Move constructor  
Destructor  
Move constructor  
Destructor  
Constructor  
Move constructor  
Destructor  
Move constructor  
Destructor  
Constructor  
Move constructor  
Destructor  
Destructor  
Destructor  
Destructor  
Destructor  

यहां तक ​​कि जब कॉपी एलिसन होता है और कॉपी- / मूव-कंस्ट्रक्टर को नहीं बुलाया जाता है, तो उसे मौजूद और एक्सेसिबल होना चाहिए (जैसे कि कोई ऑप्टिमाइज़ेशन बिल्कुल भी नहीं हुआ हो), अन्यथा प्रोग्राम बीमार हो जाता है।

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

#include <iostream>     
int n = 0;    
class ABC     
{  public:  
 ABC(int) {}    
 ABC(const ABC& a) { ++n; } // the copy constructor has a visible side effect    
};                     // it modifies an object with static storage duration    

int main()   
{  
  ABC c1(21); // direct-initialization, calls C::C(42)  
  ABC c2 = ABC(21); // copy-initialization, calls C::C( C(42) )  

  std::cout << n << std::endl; // prints 0 if the copy was elided, 1 otherwise
  return 0;  
}

Output without -fno-elide-constructors  
root@ajay-PC:/home/ayadav# g++ -std=c++11 copy_elision.cpp  
root@ajay-PC:/home/ayadav# ./a.out   
0

Output with -fno-elide-constructors  
root@ajay-PC:/home/ayadav# g++ -std=c++11 copy_elision.cpp -fno-elide-constructors  
root@ajay-PC:/home/ayadav# ./a.out   
1

जीसीसी प्रदान करता है -fno-elide-constructors कॉपी एलीशन को निष्क्रिय करने विकल्प । यदि आप संभावित नकल से बचना चाहते हैं, तो उपयोग करें -fno-elide-constructors

अब लगभग सभी कंपाइलर ऑप्टिमाइज़ेशन सक्षम होने पर कॉपी एलीगेशन प्रदान करते हैं (और यदि कोई अन्य विकल्प इसे अक्षम करने के लिए सेट नहीं है)।

निष्कर्ष

प्रत्येक कॉपी एलीज़न के साथ, एक निर्माण और कॉपी के एक मिलान विनाश को छोड़ दिया जाता है, इस प्रकार सीपीयू समय की बचत होती है, और एक ऑब्जेक्ट नहीं बनता है, इस प्रकार स्टैक फ्रेम पर जगह की बचत होती है।


6
यह कथन ABC obj2(xyz123());NRVO या RVO है? क्या यह अस्थायी चर / वस्तु के समान नहीं है ABC xyz = "Stack Overflow";//RVO
आसिफ मुश्ताक

3
आरवीओ का अधिक ठोस चित्रण करने के लिए, आप असेंबली को संदर्भित कर सकते हैं कि कंपाइलर जनरेट करता है (कंपाइलर फ्लैग को बदलें -fno-elide-constructors diff को देखने के लिए)। godbolt.org/g/Y2KcdH
Gab 好人 '
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.