मेरा मानना है कि आपके पास सही अवलोकन है लेकिन गलत व्याख्या है!
मूल्य वापस करने से कॉपी नहीं होगी, क्योंकि प्रत्येक सामान्य चतुर कंपाइलर इस मामले में (एन) आरवीओ का उपयोग करेगा । C ++ 17 से यह अनिवार्य है, इसलिए आप फ़ंक्शन से स्थानीय जनित वेक्टर को वापस करके कोई भी कॉपी नहीं देख सकते हैं।
ठीक है, चलो std::vector
निर्माण के दौरान या इसे चरण दर चरण भरने के साथ थोड़ा सा खेल देगा।
सबसे पहले, एक डेटा प्रकार उत्पन्न करने देता है जो हर कॉपी या इस तरह दिखाई देता है:
template <typename DATA >
struct VisibleCopy
{
private:
DATA data;
public:
VisibleCopy( const DATA& data_ ): data{ data_ }
{
std::cout << "Construct " << data << std::endl;
}
VisibleCopy( const VisibleCopy& other ): data{ other.data }
{
std::cout << "Copy " << data << std::endl;
}
VisibleCopy( VisibleCopy&& other ) noexcept : data{ std::move(other.data) }
{
std::cout << "Move " << data << std::endl;
}
VisibleCopy& operator=( const VisibleCopy& other )
{
data = other.data;
std::cout << "copy assign " << data << std::endl;
}
VisibleCopy& operator=( VisibleCopy&& other ) noexcept
{
data = std::move( other.data );
std::cout << "move assign " << data << std::endl;
}
DATA Get() const { return data; }
};
और अब कुछ प्रयोग शुरू करते हैं:
using T = std::vector< VisibleCopy<int> >;
T Get1()
{
std::cout << "Start init" << std::endl;
std::vector< VisibleCopy<int> > vec{ 1,2,3,4 };
std::cout << "End init" << std::endl;
return vec;
}
T Get2()
{
std::cout << "Start init" << std::endl;
std::vector< VisibleCopy<int> > vec(4,0);
std::cout << "End init" << std::endl;
return vec;
}
T Get3()
{
std::cout << "Start init" << std::endl;
std::vector< VisibleCopy<int> > vec;
vec.emplace_back(1);
vec.emplace_back(2);
vec.emplace_back(3);
vec.emplace_back(4);
std::cout << "End init" << std::endl;
return vec;
}
T Get4()
{
std::cout << "Start init" << std::endl;
std::vector< VisibleCopy<int> > vec;
vec.reserve(4);
vec.emplace_back(1);
vec.emplace_back(2);
vec.emplace_back(3);
vec.emplace_back(4);
std::cout << "End init" << std::endl;
return vec;
}
int main()
{
auto vec1 = Get1();
auto vec2 = Get2();
auto vec3 = Get3();
auto vec4 = Get4();
// All data as expected? Lets check:
for ( auto& el: vec1 ) { std::cout << el.Get() << std::endl; }
for ( auto& el: vec2 ) { std::cout << el.Get() << std::endl; }
for ( auto& el: vec3 ) { std::cout << el.Get() << std::endl; }
for ( auto& el: vec4 ) { std::cout << el.Get() << std::endl; }
}
हम क्या देख सकते हैं:
उदाहरण 1) हम एक प्रारंभिक सूची से एक वेक्टर बनाते हैं और शायद हम उम्मीद करते हैं कि हम 4 बार निर्माण और 4 चाल देखेंगे। लेकिन हमें 4 प्रतियाँ मिलती हैं! यह थोड़ा रहस्यमय लगता है, लेकिन इसका कारण इनिशियलाइज़र सूची का कार्यान्वयन है! बस इसे सूची से स्थानांतरित करने की अनुमति नहीं है क्योंकि सूची से पुनरावृत्त एक ऐसा है const T*
जो इससे तत्वों को स्थानांतरित करना असंभव बनाता है। इस विषय पर एक विस्तृत जवाब यहां पाया जा सकता है: initializer_list और शब्दार्थ को स्थानांतरित करें
उदाहरण 2) इस मामले में, हमें एक प्रारंभिक निर्माण और मूल्य की 4 प्रतियां मिलती हैं। यह कुछ खास नहीं है और हम यही उम्मीद कर सकते हैं।
उदाहरण 3) यहाँ भी, हम निर्माण और उम्मीद के अनुसार कुछ चालें हैं। मेरे stl कार्यान्वयन के साथ वेक्टर हर बार कारक 2 से बढ़ता है। इसलिए हम एक पहला निर्माण देखते हैं, दूसरा एक और क्योंकि वेक्टर 1 से 2 तक आकार लेता है, हम पहले तत्व की चाल देखते हैं। 3 एक को जोड़ते समय, हम 2 से 4 का आकार बदलते हैं, जिसमें पहले दो तत्वों की एक चाल की आवश्यकता होती है। जैसी कि उम्मीद थी!
उदाहरण 4) अब हम स्थान आरक्षित करते हैं और बाद में भरते हैं। अब हमारे पास कोई प्रतिलिपि नहीं है और कोई कदम नहीं है!
सभी मामलों में, हम किसी भी कदम को नहीं देखते हैं और न ही वेक्टर को वापस कॉल करने वाले पर वापस करके कॉपी करते हैं! (एन) आरवीओ जगह ले रहा है और इस कदम में आगे की कार्रवाई की आवश्यकता नहीं है!
अपने प्रश्न पर वापस जाएं:
"कैसे सी + + नकली प्रतिलिपि संचालन खोजने के लिए"
जैसा कि ऊपर देखा गया है, आप डिबगिंग उद्देश्य के लिए बीच में एक प्रॉक्सी क्लास लगा सकते हैं।
कॉपी-कॉटर को निजी बनाना कई मामलों में काम नहीं कर सकता है, क्योंकि आपके पास कुछ वांछित प्रतियां और कुछ छिपे हुए हो सकते हैं। जैसा कि ऊपर, उदाहरण के लिए केवल 4 कोड एक निजी कॉपी-कॉटर के साथ काम करेगा! और मैं इस सवाल का जवाब नहीं दे सकता, अगर उदाहरण 4 सबसे तेज़ है, क्योंकि हम शांति से शांति भरते हैं।
क्षमा करें कि मैं यहां "अवांछित" प्रतियां खोजने के लिए एक सामान्य समाधान नहीं दे सकता। यहां तक कि अगर आप कॉल के लिए अपना कोड खोदते हैं memcpy
, तो भी memcpy
आपको सभी नहीं मिलेंगे क्योंकि यह भी अनुकूलित हो जाएगा और आप सीधे कुछ असेंबलर निर्देशों को अपने लाइब्रेरी memcpy
फ़ंक्शन को कॉल किए बिना काम करते हुए देखेंगे ।
मेरा संकेत ऐसी छोटी समस्या पर ध्यान केंद्रित करना नहीं है। यदि आपके पास वास्तविक प्रदर्शन के मुद्दे हैं, तो एक प्रोफाइलर लें और मापें। बहुत सारे संभावित प्रदर्शन हत्यारे हैं, जो कि बहुत अधिक समय के लिए memcpy
उपयोग पर खर्च करते हैं, ऐसा विचार योग्य नहीं है।
std::vector
किसी भी माध्यम से कॉपी करना वह नहीं है जो उसे होना चाहिए । आपका उदाहरण एक स्पष्ट प्रतिलिपि दिखाता है, और यह केवल प्राकृतिक है, औरstd::move
फ़ंक्शन को लागू करने के लिए सही दृष्टिकोण, (फिर से imho) जैसा कि आप खुद को सुझाव देते हैं कि यदि कोई कॉपी नहीं है तो आप क्या चाहते हैं। ध्यान दें कि कुछ कंप्रेशर्स नकल को छोड़ सकते हैं यदि अनुकूलन झंडे को चालू किया जाता है, और वेक्टर अपरिवर्तित होता है।