2009-03-12 5 views
16

मुझे लगता है कि इस से पहले पूछा गया है, लेकिन मैं इसे SO पर नहीं ढूंढ पा रहा हूं, और न ही मुझे Google पर कुछ भी उपयोगी मिल सकता है। हो सकता है कि "covariant" वह शब्द नहीं है जिसे मैं ढूंढ रहा हूं, लेकिन यह अवधारणा कार्यों पर कॉन्वेंट रिटर्न प्रकारों के समान ही है, इसलिए मुझे लगता है कि यह शायद सही है। यहाँ मैं क्या करना चाहते हैं और यह मुझे एक संकलक त्रुटि देता है:सी ++ कॉन्वेंट टेम्पलेट

class Base; 
class Derived : public Base; 

SmartPtr<Derived> d = new Derived; 
SmartPtr<Base> b = d; // compiler error 

मान लें उन वर्गों पूरी तरह से बाहर fleshed कर रहे हैं ... मैं तुम्हें अंदाजा हो लगता है। यह किसी अस्पष्ट कारण के लिए को SmartPtr<Base> में परिवर्तित नहीं कर सकता है। मुझे याद है कि यह सी ++ और कई अन्य भाषाओं में सामान्य है, हालांकि फिलहाल मुझे याद नहीं है कि क्यों।

मेरा मूल प्रश्न यह है: इस असाइनमेंट ऑपरेशन को करने का सबसे अच्छा तरीका क्या है? वर्तमान में, मैं पॉइंटर को SmartPtr से बाहर खींच रहा हूं, इसे मूल प्रकार के लिए स्पष्ट रूप से ऊपर ला रहा है, फिर उचित प्रकार के नए SmartPtr में लपेट रहा हूं (ध्यान दें कि यह संसाधनों को लीक नहीं कर रहा है क्योंकि हमारे घर से उगाए गए SmartPtr कक्षा घुसपैठ संदर्भ का उपयोग करती है बढ़ रहा है)। यह लंबा और गन्दा है, खासकर जब मुझे SmartPtr को किसी अन्य ऑब्जेक्ट में लपेटने की आवश्यकता है ... कोई शॉर्टकट?

उत्तर

11

कॉपी कॉपी कन्स्ट्रक्टर और असाइनमेंट ऑपरेटर दोनों एक अलग प्रकार का स्मार्टप्रेट लेने और पॉइंटर को एक से दूसरे में कॉपी करने का प्रयास करने में सक्षम होना चाहिए। यदि प्रकार संगत नहीं हैं, तो संकलक शिकायत करेगा, और यदि वे संगत हैं, तो आपने अपनी समस्या हल कर ली है। कुछ इस तरह:

template <class T> 
class SmartPtr 
{ 
    T *ptr; 
public: 

    // Note that this IS NOT a copy constructor, just another constructor that takes 
    // a similar looking class. 
    template <class O> 
    SmartPtr(const SmartPtr<O> &src) 
    { 
     ptr = src.GetPtr(); 
    } 
    // And likewise with assignment operator. 
}; 

टी और ओ प्रकार के संगत हैं, यह काम करेंगे, यदि:

template<class Type> class SmartPtr 
{ 
    .... 
    template<class OtherType> SmartPtr(const SmartPtr<OtherType> &blah) // same logic as the SmartPtr<Type> copy constructor 

    template<class OtherType> SmartPtr<Type> &operator=(const SmartPtr<OtherType> &blah) // same logic as the SmartPtr<Type> assignment operator 
}; 
+0

टिप्पणियां छिपाएं @MSN: मैंने परीक्षण-और-त्रुटि से सीखा है कि यह "सामान्य" प्रतिलिपि निर्माता और असाइनमेंट ऑपरेटर को पूरा करने के लिए पर्याप्त नहीं है। इसलिए आपको दोनों को कार्यान्वित करना होगा: स्मार्टपीआरटी (कॉन्स स्मार्टप्रेट और) और टेम्पलेट <क्लास अन्य टाइप> स्मार्टपीआरटी (कॉन्स स्मार्टपीआर और) (op = के लिए समान) – mmmmmmmm

+0

ठीक है, यही मेरा मतलब है :) – MSN

+0

सी ++ 11 से आगे आप चालक-कन्स्ट्रक्टर और चाल-असाइनमेंट भी जोड़ते हैं ('&&'' वाले)। –

3

SmartPtr कक्षा पर निर्भर करता है। यदि इसमें एक कॉपी कन्स्ट्रक्टर (या आपके मामले में, असाइनमेंट ऑपरेटर) है जो SmartPtr<T> लेता है, जहां टी उस प्रकार का निर्माण होता है, तो यह काम नहीं करेगा, क्योंकि SmartPtr<T1>SmartPtr<T2> से संबंधित नहीं है, भले ही टी 1 और टी 2 विरासत से संबंधित है।

हालांकि, अगर SmartPtr एक templatized प्रतिलिपि निर्माता/असाइनमेंट ऑपरेटर, टेम्पलेट पैरामीटर TOther, कि SmartPtr<TOther> स्वीकार करता है के साथ है, तो यह काम करना चाहिए।

+0

यह शानदार लगता है ... मुझे यह देखने के लिए यह ASAP जांचना होगा कि यह काम करेगा या नहीं। दुर्भाग्यवश, मैं एमएसवीसी 6 का उपयोग कर रहा हूं, जिसमें प्रमुख टेम्पलेट मुद्दे हैं और आईआईआरसी यह templatized वर्गों के अंदर templatized कार्यों पर barf होगा, भले ही आप इसे कॉल करते समय टेम्पलेट पैरामीटर स्पष्ट रूप से निर्दिष्ट करते हैं। – rmeador

+0

वास्तव में? - मुझे पता है मैंने एमएसवीसी 6 के तहत यह सटीक चीज की है। – Eclipse

+0

मैंने एक स्मार्टपीआरआर कक्षा लिखी जो एमएसवीसी 6 के तहत यह अधिकार करता है, इसलिए आपको ठीक होना चाहिए। –

2

मान लिया जाये कि आप SmartPtr वर्ग के नियंत्रण है, समाधान एक टेम्प्लेटेड निर्माता प्रदान करना है वे नहीं हैं आपको एक संकलन त्रुटि मिलेगी।

+0

यह हमेशा इतना आसान नहीं होता है; आपको यह सुनिश्चित करने की आवश्यकता है कि उचित संदर्भ गणना बनाए रखा जाए। आखिरी पीआरटी के बाद व्युत्पन्न ऑब्जेक्ट को हटा नहीं सकता है जब भी उसी व्युत्पन्न में Ptr अभी भी हैं। – MSalters

+0

ठीक है, हाँ, आपको अभी भी यह सुनिश्चित करना होगा कि आप वास्तव में स्मार्ट पॉइंटर के स्मारक को लागू करते हैं। – Eclipse

15

SmartPtr<Base> और SmartPtr<Derived>SmartPtr टेम्पलेट के दो अलग-अलग तत्काल हैं। ये नए वर्ग विरासत को साझा नहीं करते हैं जो Base और Derived करते हैं। इसलिए, आपकी समस्या।

what is the best way to perform this assignment operation?

SmartPtr<Base> b = d; 

असाइनमेंट ऑपरेटर आह्वान नहीं करता है। यह कॉपी-ctor का आह्वान (कॉपी ज्यादातर मामलों में elided जाता है) और बिल्कुल के रूप में करता है, तो आप ने लिखा है:

SmartPtr<Base> b(d); 

एक कॉपी-ctor कि एक SmartPtr<OtherType> लेता है और इसे लागू करने के लिए प्रदान करें। असाइनमेंट ऑपरेटर के लिए ही चला जाता है। SmartPtr के अर्थशास्त्र को ध्यान में रखते हुए आपको कॉपी-सीटर और सेशन लिखना होगा।

+0

मुझे पता है कि यह काम नहीं करता है, और अब मैं फिर से जागरूक हूं (धन्यवाद) कि यह दो टेम्पलेट वर्गों के बीच संबंधों की कमी के कारण है। मैं पूछ रहा हूं कि इसे कैसे व्यवहार किया जाए जैसे कि वे संबंधित हैं, शायद कुछ चालाक सीटीओ या अन्य चालबाजी जोड़कर? – rmeador

+2

बहुत कुछ SmartPtr क्लास के सटीक अर्थशास्त्र पर निर्भर करता है उदा। क्या इसमें स्वामित्व का हस्तांतरण है, क्या यह संदर्भ गिनती इत्यादि करता है। आपको SmartPtr के अर्थशास्त्र को ध्यान में रखते हुए कॉपी-सीटर और सेशन लिखना होगा। – dirkgently

5

टेम्पलेट्स कॉन्वेंट नहीं हैं, और यह अच्छा है; कल्पना क्या निम्नलिखित मामले में क्या होगा:

vector<Apple*> va; 
va.push_back(new Apple); 

// Now, if templates were covariants, a vector<Apple*> could be 
// cast to a vector<Fruit*> 
vector<Fruit*> & vf = va; 
vf.push_back(new Orange); // Bam, we just added an Orange among the Apples! 

प्राप्त करने के लिए आपको बस इतना करना कोशिश कर रहे हैं, SmartPointer वर्ग एक templatized निर्माता, है कि या तो एक और SmartPointer या किसी अन्य प्रकार का एक सूचक लेता होना आवश्यक है। आप boost :: shared_ptr पर एक नज़र डाल सकते हैं, जो वास्तव में करता है।

template <typename T> 
class SmartPointer { 

    T * ptr; 

    public: 
    SmartPointer(T * p) : ptr(p) {} 
    SmartPointer(const SmartPointer & sp) : ptr(sp.ptr) {} 

    template <typename U> 
    SmartPointer(U * p) : ptr(p) {} 

    template <typename U> 
    SmartPointer(const SmartPointer<U> & sp) : ptr(sp.ptr) {} 

    // Do the same for operator= (even though it's not used in your example) 
}; 
+0

आप अपने उदाहरण के साथ एक उत्कृष्ट बिंदु बनाते हैं ... मैंने इसे नहीं माना था। स्मार्ट पॉइंटर के मामले में, मुझे नहीं लगता कि आप उस समस्या में भाग लेते हैं क्योंकि क्लास इंटरफेस अनिवार्य रूप से पॉइंट-टू टाइप इंटरफ़ेस (अधिभारित -> और * के माध्यम से) जैसा ही है। – rmeador

+0

मुझे लगता है कि आपका उदाहरण एक गलत उदाहरण है कि वास्तव में यह वास्तव में covariant नहीं है _because_ आप सूची में ऑरेंज की तरह कुछ निकाल सकते हैं। यह केवल तभी होगा जब सार्वजनिक इंटरफ़ेस केवल _returns_ Fruit *, लेकिन _accepts_ Fruit * नहीं होगा। सी # क्रमशः जेनेरिक प्रकारों को covariant या contravariant बनाने के लिए "इन" और "आउट" कीवर्ड प्रदान करता है। यह सांख्यिकीय रूप से लागू करता है कि आपके द्वारा वर्णित स्थिति से बचने के लिए प्रकारों का उपयोग कैसे किया जाता है। – LostSalad

0

मुझे लगता है कि सबसे आसान काम करने के लिए निम्न अनुसार एक और SmartPtr के लिए स्वत: रूपांतरण प्रदान करना है:

template <class T> 
class SmartPtr 
{ 
public: 
    SmartPtr(T *ptr) { t = ptr; } 
    operator T *() const { return t; } 
    template <class Q> operator SmartPtr<Q>() const 
    { return SmartPtr<Q>(static_cast<Q *>(static_cast<T *>(* this))); } 
private: 
    T *t; 
}; 

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

+0

{वापसी SmartPtr (टी) का प्रयास करें; } कंपाइलर आपको बताएगा कि सभी टी के बिना क्यू * को टी * असाइन किया जा सकता है। सुनिश्चित करें कि आपका संदर्भ गिनती तर्क टेम्पलेट प्रकारों के बीच संदर्भ गणना साझा कर सकता है। एक int * संदर्भ गणना करने में सक्षम होना चाहिए। – jmucchiello