मैं विरासत कोड के कुछ टुकड़े के लिए एक आवरण है।
class A{
L* impl_; // the legacy object has to be in the heap, could be also unique_ptr
A(A const&) = delete;
L* duplicate(){L* ret; legacy_duplicate(impl_, &L); return ret;}
... // proper resource management here
};
इस विरासत कोड में, वह फ़ंक्शन जो "ऑब्जेक्ट को डुप्लिकेट करता है" थ्रेड सुरक्षित नहीं है (उसी तर्क पर कॉल करते समय), इसलिए यह constआवरण में चिह्नित नहीं है । मुझे लगता है कि आधुनिक नियमों का पालन करना: https://herbsutter.com/2013/01/01/video-you-dont-know-const-and-mutable/
यह duplicateएक कॉपी कंस्ट्रक्टर को लागू करने के लिए एक अच्छा तरीका है, सिवाय इसके कि यह नहीं है const। इसलिए मैं सीधे यह नहीं कर सकता:
class A{
L* impl_; // the legacy object has to be in the heap
A(A const& other) : L{other.duplicate()}{} // error calling a non-const function
L* duplicate(){L* ret; legacy_duplicate(impl_, &ret); return ret;}
};
तो इस विरोधाभासी स्थिति से बाहर निकलने का रास्ता क्या है?
(मान लीजिए कि यह legacy_duplicateथ्रेड-सुरक्षित नहीं है, लेकिन मुझे पता है कि जब यह बाहर निकलता है, तो मूल स्थिति में वस्तु छोड़ देता है। C-फ़ंक्शन होने के कारण व्यवहार केवल प्रलेखित होता है, लेकिन कब्ज की कोई अवधारणा नहीं होती है।)
मैं कई संभावित परिदृश्यों के बारे में सोच सकता हूं:
(1) एक संभावना यह है कि सामान्य अर्थ के साथ कॉपी कंस्ट्रक्टर को लागू करने का कोई तरीका नहीं है। (हां, मैं ऑब्जेक्ट को स्थानांतरित कर सकता हूं और यह वह नहीं है जो मुझे चाहिए।)
(२) दूसरी ओर, किसी वस्तु की प्रतिलिपि बनाना इस अर्थ में गैर-थ्रेड-सेफ़ है कि सरल प्रकार की प्रतिलिपि बनाने से स्रोत को अर्ध-संशोधित अवस्था में पाया जा सकता है, इसलिए मैं बस आगे बढ़ सकता हूं और ऐसा कर सकता हूं,
class A{
L* impl_;
A(A const& other) : L{const_cast<A&>(other).duplicate()}{} // error calling a non-const function
L* duplicate(){L* ret; legacy_duplicate(impl_, &ret); return ret;}
};
(3) या यहां तक कि सिर्फ duplicateकॉन्स्टेबल की घोषणा करें और सभी संदर्भों में थ्रेड सुरक्षा के बारे में झूठ बोलें। (सभी लीगेसी फ़ंक्शन के बारे में परवाह नहीं है constइसलिए कंपाइलर भी शिकायत नहीं करेगा।)
class A{
L* impl_;
A(A const& other) : L{other.duplicate()}{}
L* duplicate() const{L* ret; legacy_duplicate(impl_, &ret); return ret;}
};
(4) अंत में, मैं तर्क का अनुसरण कर सकता हूं और एक गैर-कांस्टेबल तर्क लेने वाला एक कॉपी-कंस्ट्रक्टर बना सकता हूं ।
class A{
L* impl_;
A(A const&) = delete;
A(A& other) : L{other.duplicate()}{}
L* duplicate(){L* ret; legacy_duplicate(impl_, &ret); return ret;}
};
यह पता चला है कि यह कई संदर्भों में काम करता है, क्योंकि ये ऑब्जेक्ट आमतौर पर नहीं होते हैं const।
सवाल यह है कि क्या यह एक वैध या सामान्य मार्ग है?
मैं उनका नाम नहीं ले सकता, लेकिन मैं सहजता से उम्मीद करता हूं कि नॉन-कास्ट कॉपी कंस्ट्रक्टर होने की राह में बहुत सारी समस्याएं हैं। संभवतः इस सूक्ष्मता के कारण यह मूल्य-प्रकार के रूप में योग्य नहीं होगा।
(५) अंत में, हालांकि यह एक ओवरकिल लगता है और इसमें एक स्थिर रनटाइम लागत हो सकती है, मैं एक म्यूटेंट जोड़ सकता हूं:
class A{
L* impl_;
A(A const& other) : L{other.duplicate_locked()}{}
L* duplicate(){
L* ret; legacy_duplicate(impl_, &ret); return ret;
}
L* duplicate_locked() const{
std::lock_guard<std::mutex> lk(mut);
L* ret; legacy_duplicate(impl_, &ret); return ret;
}
mutable std::mutex mut;
};
लेकिन ऐसा करने के लिए मजबूर होना निराशावाद जैसा लगता है और वर्ग को बड़ा बनाता है। मुझे यकीन नहीं है। मैं वर्तमान में (4) , या (5) या दोनों के संयोजन की ओर झुक रहा हूं ।
संपादित करें 1:
एक अन्य विकल्प:
(6) डुप्लिकेट सदस्य फ़ंक्शन के सभी गैर-समझ के बारे में भूल जाओ और बस legacy_duplicateकंस्ट्रक्टर से कॉल करें और घोषणा करें कि कॉपी कंस्ट्रक्टर थ्रेड सुरक्षित नहीं है। (और यदि आवश्यक हो तो प्रकार का एक और धागा-सुरक्षित वर्जन बनाएं A_mt)
class A{
L* impl_;
A(A const& other){legacy_duplicate(other.impl_, &impl_);}
};
संपादित करें 2:
यह एक अच्छा मॉडल हो सकता है कि विरासत समारोह क्या करता है। ध्यान दें कि इनपुट को स्पर्श करके कॉल पहले तर्क द्वारा दर्शाए गए मूल्य के संबंध में सुरक्षित नहीं है।
void legacy_duplicate(L* in, L** out){
*out = new L{};
char tmp = in[0];
in[0] = tmp;
std::memcpy(*out, in, sizeof *in); return;
}
EDIT 3:
मैंने हाल ही में सीखा कि std::auto_ptrएक गैर-कॉन्स्टेबल "कॉपी" कंस्ट्रक्टर होने की एक समान समस्या थी। इसका प्रभाव यह था कि auto_ptrएक कंटेनर के अंदर इस्तेमाल नहीं किया जा सकता था । https://www.quantstart.com/articles/STL-Containers-and-Auto_ptrs-Why-They-Dont-Mix/
legacy_duplicateको दो अलग-अलग थ्रेड्स से एक ही पहले तर्क के साथ नहीं कहा जा सकता है।
constवास्तव में इसका क्या मतलब है। :-) मुझे लगता है कि const&जब तक मैं संशोधित नहीं करता, तब तक मैं अपनी कॉपी ctor में लेने के बारे में दो बार नहीं सोचूंगा other। मैं हमेशा थ्रेड सेफ्टी के बारे में सोचता हूं, क्योंकि जो कुछ भी कई थ्रेड्स से एक्सेस करने की जरूरत होती है, उसके ऊपर एनकैप्सुलेशन के माध्यम से जुड़ता है, और मैं वास्तव में उत्तर की प्रतीक्षा कर रहा हूं।
Lजो एक नयाLउदाहरण बनाकर संशोधित किया गया है ? यदि नहीं, तो आप क्यों मानते हैं कि यह ऑपरेशन थ्रेड-सुरक्षित नहीं है?