2012-12-11 10 views
5

स्थानांतरित करें मैंने हाल ही में टाइप एरर के बारे में सीखना शुरू कर दिया है। यह पता चला कि यह तकनीक मेरे जीवन को बहुत सरल बना सकती है। इस प्रकार मैंने इस पैटर्न को लागू करने की कोशिश की। हालांकि, मुझे टाइप एरर क्लास की कॉपी- और मूव-कन्स्ट्रक्टर के साथ कुछ समस्याएं आती हैं। अब, कोड पर एक नज़र है, जो काफी सीधे आगेसी ++ टाइप टेम्पलेट प्रतिलिपि के साथ मिटाएं और कन्स्ट्रक्टर

#include<iostream> 
class A //first class 
{ 
    private: 
     double _value; 
    public: 
     //default constructor 
     A():_value(0) {} 
     //constructor 
     A(double v):_value(v) {} 
     //copy constructor 
     A(const A &o):_value(o._value) {} 
     //move constructor 
     A(A &&o):_value(o._value) { o._value = 0; } 

     double value() const { return _value; } 
}; 

class B //second class 
{ 
    private: 
     int _value; 
    public: 
     //default constructor 
     B():_value(0) {} 
     //constructor 
     B(int v):_value(v) {} 
     //copy constructor 
     B(const B &o):_value(o._value) {} 
     //move constructor 
     B(B &&o):_value(o._value) { o._value = 0; } 

     //some public member 
     int value() const { return _value; } 
}; 

class Erasure //the type erasure 
{ 
    private: 
     class Interface //interface of the holder 
     { 
      public: 
       virtual double value() const = 0; 
     }; 

     //holder template - implementing the interface 
     template<typename T> class Holder:public Interface 
     { 
      public: 
       T _object; 
      public: 
       //construct by copying o 
       Holder(const T &o):_object(o) {} 
       //construct by moving o 
       Holder(T &&o):_object(std::move(o)) {} 
       //copy constructor 
       Holder(const Holder<T> &o):_object(o._object) {} 
       //move constructor 
       Holder(Holder<T> &&o):_object(std::move(o._object)) {} 

       //implements the virtual member function 
       virtual double value() const 
       { 
        return double(_object.value()); 
       } 
     }; 

     Interface *_ptr; //pointer to holder 
    public: 
     //construction by copying o 
     template<typename T> Erasure(const T &o): 
      _ptr(new Holder<T>(o)) 
     {} 

     //construction by moving o 
     template<typename T> Erasure(T &&o): 
      _ptr(new Holder<T>(std::move(o))) 
     {} 

     //delegate 
     double value() const { return _ptr->value(); } 
}; 

int main(int argc,char **argv) 
{ 
    A a(100.2344); 
    B b(-100); 

    Erasure g1(std::move(a)); 
    Erasure g2(b); 

    return 0; 
} 

है एक संकलक मैं जीसीसी 4.7 का प्रयोग कर एक डेबियन परीक्षण सिस्टम पर के रूप में प्राप्त हो जाता है पहले। कोड मान लिया जाये कि terasure.cpp निर्माण नामक फ़ाइल में संग्रहीत किया जाता है निम्न त्रुटि संदेश

$> g++ -std=c++0x -o terasure terasure.cpp 
terasure.cpp: In instantiation of ‘class Erasure::Holder<B&>’: 
terasure.cpp:78:45: required from ‘Erasure::Erasure(T&&) [with T = B&]’ 
terasure.cpp:92:17: required from here 
terasure.cpp:56:17: error: ‘Erasure::Holder<T>::Holder(T&&) [with T = B&]’ cannot be overloaded 
terasure.cpp:54:17: error: with ‘Erasure::Holder<T>::Holder(const T&) [with T = B&]’ 
terasure.cpp: In instantiation of ‘Erasure::Erasure(T&&) [with T = B&]’: 
terasure.cpp:92:17: required from here 
terasure.cpp:78:45: error: no matching function for call to ‘Erasure::Holder<B&>::Holder(std::remove_reference<B&>::type)’ 
terasure.cpp:78:45: note: candidates are: 
terasure.cpp:60:17: note: Erasure::Holder<T>::Holder(Erasure::Holder<T>&&) [with T = B&] 
terasure.cpp:60:17: note: no known conversion for argument 1 from ‘std::remove_reference<B&>::type {aka B}’ to ‘Erasure::Holder<B&>&&’ 
terasure.cpp:58:17: note: Erasure::Holder<T>::Holder(const Erasure::Holder<T>&) [with T = B&] 
terasure.cpp:58:17: note: no known conversion for argument 1 from ‘std::remove_reference<B&>::type {aka B}’ to ‘const Erasure::Holder<B&>&’ 
terasure.cpp:54:17: note: Erasure::Holder<T>::Holder(const T&) [with T = B&] 
terasure.cpp:54:17: note: no known conversion for argument 1 from ‘std::remove_reference<B&>::type {aka B}’ to ‘B&’ 

ऐसा लगता है कि Erasure g2(b); के लिए संकलक अभी भी चाल निर्माता का उपयोग करने की कोशिश करता है की ओर जाता है। क्या यह संकलक का इरादा व्यवहार है? क्या मैं टाइप एरर पैटर्न के साथ सामान्य रूप से कुछ गलत समझता हूं? क्या किसी को यह पता है कि यह अधिकार कैसे प्राप्त करें?

+1

आपने खजाना गलत लिखा है –

उत्तर

3

कंपाइलर त्रुटियों से स्पष्ट होने के नाते, संकलक T = B& के लिए Holder कक्षा को तुरंत चालू करने का प्रयास कर रहा है। इसका मतलब है कि कक्षा एक संदर्भ प्रकार के सदस्य को स्टोर करेगी, जो आपको प्रतिलिपि और ऐसी कुछ समस्याएं देती है।

समस्या इस तथ्य में निहित है कि T&& (प्रयुक्त टेम्पलेट तर्कों के लिए) एक सार्वभौमिक संदर्भ है, जिसका अर्थ है कि यह सब कुछ से बंधेगा। B के आर-मूल्यों के लिए यह T निकालना होगा B हो सकता है और एक आर मूल्य संदर्भ के रूप में बाँध यह T निकालना होगा B& हो सकता है और B& && रूप B& (const B एल-मूल्यों के लिए व्याख्या करने के लिए टूट संदर्भ का उपयोग करने के एल-मूल्यों के लिए, करने के लिए यह होगा Tconst B& होने के लिए घटाएं और ढहने को करें)। आपके उदाहरण में b एक संशोधित एल-वैल्यू है, जिससे कन्स्ट्रक्टर T&& (B& होने के लिए घटाया गया है) const T& (const B& होने के लिए घटाया गया) एक बेहतर मैच है। इसका मतलब यह भी है कि Erasure कन्स्ट्रक्टर const T& लेना कन्स्ट्रक्टर वास्तव में आवश्यक नहीं है (Holder के लिए T के कारण उस कन्स्ट्रक्टर के लिए नहीं लिया जा रहा है)।

इसका समाधान आपके धारक वर्ग बनाते समय संदर्भ से संदर्भ (और शायद स्थिरता, जब तक कि आप एक कॉन्स सदस्य नहीं चाहते) को पट्टी करना है। आपको std::move के बजाय std::forward<T> का भी उपयोग करना चाहिए, क्योंकि जैसा कि बताया गया है कि कन्स्ट्रक्टर एल-वैल्यू से भी जुड़ा हुआ है और उनसे आगे बढ़ना शायद एक बुरा विचार है।

template<typename T> Erasure(T&& o): 
     _ptr(new Holder<typename std::remove_cv<typename std::remove_reference<T>::type>::type>(std::forward<T>(o)) 
    {} 

आपके Erasure कक्षा में एक और बग, जो संकलक द्वारा पकड़ा नहीं किया जाएगा: आप की दुकान अपने Holder एक कच्चे सूचक में आबंटित स्मृति ढेर करने के लिए है, लेकिन यह न ही कस्टम से निपटने को हटाने के लिए न तो कस्टम नाशक है प्रतिलिपि/चलने/असाइनमेंट के लिए (तीन/पांच का नियम)। हल करने का एक विकल्प है कि उन परिचालनों को लागू करना होगा (या =delete का उपयोग करके अनिवार्य लोगों को मना कर दें)। हालांकि यह कुछ हद तक कठिन है, इसलिए मेरा व्यक्तिगत सुझाव स्मृति प्रबंधन के लिए मैन्युअल रूप से प्रबंधित नहीं करना होगा, लेकिन स्मृति प्रबंधन के लिए std::unique_ptr का उपयोग करने के लिए (आपको प्रतिलिपि बनाने की क्षमता नहीं दी जाएगी, लेकिन यदि आप चाहते हैं कि आपको पहले क्लोनिंग के लिए Holder कक्षा का विस्तार करने की आवश्यकता है वैसे भी)।

अन्य बिंदुओं पर विचार करने के लिए: क्यों आप Erasure::Holder<T>, A और B के लिए कस्टम कॉपी/कदम कंस्ट्रक्टर्स को लागू कर रहे हैं? डिफ़ॉल्ट वाले पूरी तरह से ठीक होना चाहिए और एक चाल असाइनमेंट ऑपरेटर की पीढ़ी को अक्षम नहीं करेगा।

एक और मुद्दा यह है कि Erasure(T &&o) समस्याग्रस्त है कि यह कॉपी/कदम निर्माता के साथ प्रतिस्पर्धा करेंगे (T&&Èrasure& करने के लिए बाध्य कर सकते हैं जो फिर एक बेहतर मैच दोनों const Erasure& और Erasure&& है)। इससे बचने के लिए आप Erasure के प्रकार के खिलाफ जांच करने के लिए, आप कुछ इसी तरह यह करने के लिए दे रही है enable_if उपयोग कर सकते हैं:

template<typename T, typename Dummy = typename std::enable_if<!std::is_same<Erasure, std::remove_reference<T>>::value>::type> 
    Erasure(T&& o): 
     _ptr(new Holder<typename std::remove_cv<typename std::remove_reference<T>::type>::type>(std::forward<T>(o)) 
    {} 
+0

मेरे देर के उत्तर के लिए खेद है और आपकी सलाह के लिए धन्यवाद। इससे समस्या हल हो गई। –

1

आपकी समस्या है कि प्रकार T अपने निर्माता एक सार्वभौमिक संदर्भ लेने के द्वारा एक संदर्भ निष्कर्ष निकाला जाता है। आप इस की तर्ज पर कुछ प्रयोग करना चाहते हैं:

#include <type_traits> 

class Erasure { 
    .... 

    //construction by moving o 
    template<typename T> 
    Erasure(T &&o): 
     _ptr(new Holder<typename std::remove_reference<T>::type>(std::forward<T>(o))) 
    { 
    } 
}; 

है, तो आप T से निष्कर्ष निकाला संदर्भ निकालने की जरूरत है (और शायद यह भी किसी भी सीवी क्वालीफायर लेकिन सुधार ऐसा नहीं करता है)। और फिर आप std::move() तर्क o लेकिन std::forward<T>() नहीं करना चाहते हैं: का उपयोग करके आप वास्तव में गैर-constErasure के निर्माता के संदर्भ में विनाशकारी परिणाम प्राप्त कर सकते हैं।

मैंने दूसरे कोड पर बहुत अधिक ध्यान नहीं दिया है, जहां तक ​​मैं कुछ अर्थपूर्ण त्रुटियों को भी बता सकता हूं (उदाहरण के लिए, आपको या तो संदर्भ गणना के कुछ रूप की आवश्यकता है या clone() इंट निहित पॉइंटर्स के रूप में, Erasure में संसाधन नियंत्रण (यानी, कॉपी कन्स्ट्रक्टर, कॉपी असाइनमेंट, और विनाशक)

संबंधित मुद्दे