2013-02-22 12 views
6

सभी,std :: unique_ptr नष्ट कर दिया समारोह, initializer_list - संचालित आवंटन

जब मैं एक विजेट सरणी का दृष्टांत एक प्रारंभकर्ता-सूची प्रारूप, एक नग्न सूचक है कि एक सदस्य-चर विजेट उदाहरण के लिए अंक का उपयोग कर संकलित लेकिन करने के लिए परिवर्तन के बाद std :: unique_ptr <> gcc हटाए गए फ़ंक्शन के संबंध में एक संकलन त्रुटि देता है।

$ uname -एक

लिनक्स .. 3.5.0-21-जेनेरिक # 32-उबंटू SMP मंगल दिसंबर 11 18:51:59 यूटीसी 2012 x86_64 x86_64 x86_64 जीएनयू/लिनक्स

$ जी ++ - -संस्करण

जी ++ (Ubuntu/Linaro 4.7.2-5ubuntu1) 4.7.2

इस कोड को निम्नलिखित संकलक त्रुटि देता है:

#include <stdlib.h> 
#include <memory> 

class Widget 
{ 
public: 
    Widget() {} 
}; 

class W1 : public Widget 
{ 
public: 
    W1() {} 
}; 

class W2 : public Widget 
{ 
public: 
    W2() {} 
}; 

class WFactory 
{ 
public: 
    WFactory(const int i) : _w(new W1()) {} 
    WFactory(const char* s) : _w(new W2()) {} 

    ~WFactory() { _w.reset(nullptr); } 
    // ~WFactory() { delete _w; } <--- for naked ptr 

private: 
    // NOTE: does not compile 
    std::unique_ptr<Widget> _w; 
    // NOTE: does compile 
    // Widget* _w; 
}; 

int main() 
{ 
    std::unique_ptr<Widget> a(new W1()); // <--- compiles fine 

    WFactory wf[] { 4, "msg" };   // <--- compiler error using unique_ptr<> 
} 

त्रुटि:

$ g++ -o unique_ptr -std=c++11 -Wall unique_ptr.cpp 
unique_ptr.cpp: In function ‘int main()’: 
unique_ptr.cpp:36:30: error: use of deleted function ‘WFactory::WFactory(const WFactory&)’ 
unique_ptr.cpp:22:7: note: ‘WFactory::WFactory(const WFactory&)’ is implicitly deleted because the default definition would be ill-formed: 
unique_ptr.cpp:22:7: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = Widget; _Dp = std::default_delete<Widget>; std::unique_ptr<_Tp, _Dp> = std::unique_ptr<Widget>]’ 
In file included from /usr/include/c++/4.7/memory:86:0, 
      from unique_ptr.cpp:2: 
/usr/include/c++/4.7/bits/unique_ptr.h:262:7: error: declared here 
unique_ptr.cpp:36:30: error: use of deleted function ‘WFactory::WFactory(const WFactory&)’ 
unique_ptr.cpp:36:14: warning: unused variable ‘wf’ [-Wunused-variable] 

मैं या तो के रूप में एक नुकसान में हूँ: पर्दे के किसी हटाई fcxn पैदावार के पीछे यांत्रिकी; या अधिक सरलता से, std :: unique_ptr < की अभिव्यक्ति क्यों w/नग्न पीआरटी की तुलना में प्रतिबंधित है।

मेरा प्रश्न है:

  • पायलट की गलती?
  • संकलक त्रुटि?
  • क्या मैं अपना इच्छित कोड w/कुछ परिवर्तन करने के लिए प्राप्त कर सकता हूं?

धन्यवाद।

संपादित करें 1

अपने जवाब है, जो मैं सराहना करते हैं, मैं WFactory के लिए निम्न परिवर्तन कर सकते हैं के आधार पर:

class WFactory 
{ 
public: 
    WFactory(const WFactory& wf) 
    { 
     (const_cast<WFactory&>(wf)).moveto(_w); 
    } 

    WFactory(const int i) : _w(new W1()) {} 
    WFactory(const char* s) : _w(new W2()) {} 

    ~WFactory() { _w.reset(nullptr); } 

    void moveto(std::unique_ptr<Widget>& w) 
    { 
     w = std::move(_w); 
    } 
private: 
    std::unique_ptr<Widget> _w; 
}; 

(के रूप में अनैतिक कोड चिह्नित किये) और अब कार्यक्रम संकलित और चलाता है। मैं सराहना करता हूं कि मानकों के लोगों ने एक कारण के लिए विनिर्देश लिखा है, इसलिए मैंने अपने मामले को मेरे मामले के लिए एक अच्छा विश्वास विशेषज्ञता के रूप में पोस्ट किया है, जहां मैं वास्तव में पीआरटी की विशिष्टता पर जोर देना चाहता हूं।

संपादित 2

जोनाथन के उत्तरों के आधार पर, निम्न कोड निहित चाल ctor को दबाने नहीं करता है:

class WFactory 
{ 
public: 
    WFactory(const int i) : _w(new W1()) {} 
    WFactory(const char* s) : _w(new W2()) {} 

private: 
    std::unique_ptr<Widget> _w; 
}; 

नोट है कि वहाँ सभी में कोई ~WFactory() {..}

शायद ya-ans है, लेकिन मुझे पता चला है कि wf [] में मुख्य() में C++ 11-style पुनरावृत्ति का उपयोग करके कोई प्रतिलिपि-ctor-for-WFactory त्रुटि वापस आती है।यह है:

int Main() 
.. 
    WFactory wf[] { 4, "msg" }; 

    for (WFactory iwf : wf) <---- compiler error again 
     // .. 

    for (unsigned i = 0; i < 2; ++i) <--- gcc happy 
     wf[i] // .. 
} 

मुझे लगता है कि यह स्पष्ट है कि नया सी ++ 11-शैली पुनरावृत्ति ऑब्जेक्ट प्रतिलिपि कर रहा है।

+0

ब्रेस्ड सूचियों के माध्यम से प्रारंभिक वस्तुओं को औपचारिक रूप से प्रतिलिपि बनाने की आवश्यकता है, दुर्भाग्यवश। –

+0

धन्यवाद, यह दिलचस्प है। प्रतिलिपि कहां है? त्रुटि इंगित करती है कि WFactory कॉपी करने योग्य होना चाहिए। मुझे वह नहीं मिला। – JayInNyc

+0

यह भाषा के विनिर्देशन में है। प्रतिलिपि वास्तव में अभ्यास में नहीं होगी, लेकिन कक्षा में अभी भी एक सुलभ प्रतिलिपि बनाने की आवश्यकता है। –

उत्तर

8

पैरा सी ++ 11 स्टैंडर्ड की 8.5.1/2 के अनुसार:

When an aggregate is initialized by an initializer list, as specified in 8.5.4, the elements of the initializer listare taken as initializers for the members of the aggregate, in increasing subscript or member order. Each member is copy-initialized from the corresponding initializer-clause. [...]

प्रत्येक तत्व के लिए, फिर, कॉपी-प्रारंभ गंतव्य प्रकार की एक अस्थायी के निर्माण है, जो तब है शामिल सरणी के तत्व की प्रतिलिपि बनाने के लिए उपयोग करें।

हालांकि, अपने वर्ग के एक सदस्य जिसका प्रकार unique_ptr का एक उदाहरण है, जो गैर copyable है शामिल हैं। इससे आपकी कक्षा गैर-प्रतिलिपि भी बन जाती है।

इसके अलावा, हालांकि unique_ptrजंगम है, अपने वर्ग नहीं है, क्योंकि संकलक द्वारा एक कदम निर्माता के अंतर्निहित पीढ़ी एक स्पष्ट रूप से परिभाषित नाशक की मौजूदगी से दबा दिया जाता है है। यदि वह मामला नहीं था (यानी, यदि आपने स्पष्ट रूप से अपनी कक्षा के लिए एक चालक कन्स्ट्रक्टर को परिभाषित किया है), प्रति-प्रारंभिक कार्य कार्य करेगा (8.5/15 देखें)।

WFactory की परिभाषा को बदलने के रूप में देखने के लिए इस प्रकार का प्रयास करें:

class WFactory 
{ 
public: 
    WFactory(const int i) : _w(new W1()) {} 
    WFactory(const char* s) : _w(new W2()) {} 
    WFactory(WFactory&& f) : _w(std::move(f._w)) {} 
    ~WFactory() { _w.reset(nullptr); } 
private: 
    std::unique_ptr<Widget> _w; 
}; 

int main() 
{ 
    std::unique_ptr<Widget> a(new W1()); 
    WFactory wf[] { 4, "msg" };   // OK 
} 
+0

कृपया ऊपर संपादित देखें देखें। फिर से धन्यवाद। – JayInNyc

+0

[dcl.init]/15 "[_ नोट: _ कॉपी-प्रारंभिकरण एक चाल (12.8) का आह्वान कर सकता है। - _end नोट] _" –

+0

@ जोनाथन वाकई: बहुत सही। मुझे संपादित करने दो। धन्यवाद। –

4

the mechanics behind the scenes that yields a deleted fcxn;

सरणी केवल ऐसे ही प्रारंभ किया जा सकता है प्रकार copyable या चल रहा है, और unique_ptr copyable, तो एक वर्ग नहीं है एक unique_ptr सदस्य के साथ डिफ़ॉल्ट रूप से copyable नहीं है, और अपने प्रकार का एक उपयोगकर्ता परिभाषित नाशक, जो निहित चाल निर्माता को रोकता है, तो अपने प्रकार या तो चल नहीं है।

or more simply, why the expressiveness of std::unique_ptr<> appears to be restricted compared w/ a naked ptr.

unique_ptr आपको गंभीर बग से बचा रहा है। नग्न पॉइंटर के साथ आपका प्रकार बड़े पैमाने पर असुरक्षित है और इसके परिणामस्वरूप अपरिभाषित व्यवहार होगा, क्योंकि आपके पास एक प्रतिलिपि बनाने वाला नहीं है, इसलिए सूचक की प्रतिलिपि बनाई जाती है और फिर दो अलग-अलग वस्तुओं द्वारा दो बार हटा दी जाती है। बूम, आपके कार्यक्रम में व्यवहार को अपरिभाषित किया गया है। unique_ptr इसे कॉपी करने से रोकने से आपकी कक्षा को ठीक करता है, जो सुरक्षित और सही है।

आप इसे कई तरीकों से काम कर सकते हैं, सबसे आसान उपयोगकर्ता परिभाषित नाशक है, जो अपनी कक्षा चल बनाता है दूर करने के लिए है, और सरणी प्रारंभ संकलित कर देगा।

या यदि आपको किसी अन्य कारण से उपयोगकर्ता द्वारा परिभाषित विनाशक की आवश्यकता है, तो भी आप _w सदस्य को स्पष्ट रूप से स्थानांतरित करने के लिए उपयोगकर्ता द्वारा परिभाषित चालक कन्स्ट्रक्टर लिखकर इसे काम कर सकते हैं।

किसी कारण यह संभव नहीं है के लिए हैं, तो आप यह एक डिफ़ॉल्ट निर्माता जोड़कर काम कर सकते हैं, तो सरणी तत्वों डिफ़ॉल्ट का निर्माण किया जा सकता है, और फिर उन्हें करने के लिए ले-बताए:

class WFactory 
{ 
public: 
    WFactory() = default; 
    WFactory(const int i) : _w(new W1()) {} 
    WFactory(const char* s) : _w(new W2()) {} 

private: 
    std::unique_ptr<Widget> _w; 
}; 

int main() 
{ 
    WFactory wf[2]; 
    wf[0] = WFactory(4); 
    wf[1] = WFactory("msg"); 
} 

आपका संपादित संस्करण अनैतिक और अत्यधिक संदिग्ध है, तो आप दूर const ऐसे ही डाली नहीं करना चाहिए और आप एक lvalue से स्थानांतरित नहीं किया जाना चाहिए, विशेष रूप से नहीं एक const lvalue। वहॉं मत जाओ।इसके बजाय बदल कैसे आप इसे कॉपी करने की जरूरत से बचने के लिए वर्ग का उपयोग करें, या मान्य प्रतिलिपि निर्माता है कि स्वामित्व वस्तु की एक गहरी प्रतिलिपि करता लिखें:

class Widget 
{ 
public: 
    Widget() {} 
    virtual std::unique_ptr<Widget> clone() const = 0; 
}; 

class W1 : public Widget 
{ 
public: 
    W1() {} 
    virtual std::unique_ptr<Widget> clone() const 
    { return std::unique_ptr<Widget>(new W1(*this)); } 
}; 

class W2 : public Widget 
{ 
public: 
    W2() {} 
    virtual std::unique_ptr<Widget> clone() const 
    { return std::unique_ptr<Widget>(new W2(*this)); } 
}; 

class WFactory 
{ 
public: 
    WFactory(const int i) : _w(new W1()) {} 
    WFactory(const char* s) : _w(new W2()) {} 
    WFactory(const WFactory& w) : _w(w._w->clone()) {} 
    // ... 

सबसे अच्छा तरीका वर्ग चल बनाने के लिए है, और ऐसा करने का एक अच्छा तरीका rule of zero

+0

मुझे यह पसंद है कि मेरा संपादन अनैतिक है ;-) यह मेरे चेहरे पर एक मुस्कान डालता है (वास्तव में)। – JayInNyc

+0

"नग्न पॉइंटर के साथ आपका प्रकार बड़े पैमाने पर असुरक्षित है और इसके परिणामस्वरूप अपरिभाषित व्यवहार होगा, क्योंकि आपके पास प्रतिलिपि बनाने वाला नहीं है इसलिए पॉइंटर कॉपी हो जाता है और फिर दो अलग-अलग ऑब्जेक्ट्स द्वारा दो बार हटा दिया जाता है। बूम, आपके प्रोग्राम ने व्यवहार को अपरिभाषित किया है। Unique_ptr इसे कॉपी करने से रोककर अपनी कक्षा को ठीक करता है, जो सुरक्षित और सही है। " - इसलिए हम सहमत हैं। यह मेरे मूल प्रश्न के लिए प्रेरणा है। – JayInNyc

+0

FYI मेरा आवेदन क्रमशः wf [0] = ..., wf [1] = ... wf [] {..} तंत्र I (वर्तमान में) आवश्यक है। – JayInNyc

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