2012-05-21 14 views
24

मैं अपनी संरचना को अद्यतन कर रहा हूं और मैं इसे एक std :: स्ट्रिंग सदस्य जोड़ना चाहता था। मूल struct इस तरह दिखता है:सी ++ 11 गैर-तुच्छ सदस्यों के साथ 11 अज्ञात संघ

struct Value { 
    uint64_t lastUpdated; 

    union { 
    uint64_t ui; 
    int64_t i; 
    float f; 
    bool b; 
    }; 
}; 

बस यूनियन के लिए एक std :: स्ट्रिंग सदस्य को जोड़ने, बेशक,, एक संकलन त्रुटि होती है क्योंकि एक सामान्य रूप से वस्तु की गैर तुच्छ कंस्ट्रक्टर्स जोड़ने के लिए की आवश्यकता होगी। हालांकि, मैं एक struct के भीतर एक गुमनाम संघ का उपयोग कर रहा

union U 
{ 
int a; 
int b; 
string s; 
U(); 
~U(); 
}; 

: In the case of std::string (text from informit.com)

Since std::string defines all of the six special member functions, U will have an implicitly deleted default constructor, copy constructor, copy assignment operator, move constructor, move assignment operator and destructor. Effectively, this means that you can't create instances of U unless you define some, or all of the special member functions explicitly.

तो वेबसाइट निम्न नमूना कोड देने के लिए चला जाता है। मैं freenode पर ## सी ++ पूछा और उन्होंने मुझे कि बजाय struct में निर्माता डाल करने के लिए था ऐसा करने के लिए सही तरीका बताया है और मुझे इस उदाहरण कोड दिया:

#include <new> 

struct Point { 
    Point() {} 
    Point(int x, int y): x_(x), y_(y) {} 
    int x_, y_; 
}; 

struct Foo 
{ 
    Foo() { new(&p) Point(); } 
    union { 
    int z; 
    double w; 
    Point p; 
    }; 
}; 

int main(void) 
{ 
} 

लेकिन वहाँ से मैं कैसे समझ नहीं सकता बाकी विशेष कार्यों को बनाएं जो std :: स्ट्रिंग को परिभाषित करने की आवश्यकता है, और इसके अलावा, मैं पूरी तरह से स्पष्ट नहीं हूं कि उस उदाहरण में ctor कैसे काम कर रहा है।

क्या मैं किसी को थोड़ा स्पष्ट समझाने के लिए किसी को प्राप्त कर सकता हूं?

+2

यह मुझे लगता है कि आपको क्या ज़रूरत है एक उचित [संस्करण] (http://www.boost.org/libs/variant/) ... – ildjarn

+1

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

उत्तर

21

यहां नए प्लेसमेंट की आवश्यकता नहीं है।

संस्करण सदस्यों को कंपाइलर से उत्पन्न कन्स्ट्रक्टर द्वारा प्रारंभ नहीं किया जाएगा, लेकिन सामान्य सीटीओआर-प्रारंभकर्ता-सूची का उपयोग करके इसे चुनने में कोई समस्या नहीं होनी चाहिए। अज्ञात यूनियनों के अंदर घोषित सदस्यों वास्तव में युक्त वर्ग के सदस्य हैं, और इन्हें कक्षा के निर्माता के रूप में प्रारंभ किया जा सकता है।

यह व्यवहार धारा 9.5 में वर्णित है।[class.union]:

A union-like class is a union or a class that has an anonymous union as a direct member. A union-like class X has a set of variant members. If X is a union its variant members are the non-static data members; otherwise, its variant members are the non-static data members of all anonymous unions that are members of X .

और खंड 12.6.2 [class.base.init] में:

A ctor-initializer may initialize a variant member of the constructor’s class. If a ctor-initializer specifies more than one mem-initializer for the same member or for the same base class, the ctor-initializer is ill-formed.

तो कोड बस हो सकता है:

#include <new> 

struct Point { 
    Point() {} 
    Point(int x, int y): x_(x), y_(y) {} 
    int x_, y_; 
}; 

struct Foo 
{ 
    Foo() : p() {} // usual everyday initialization in the ctor-initializer 
    union { 
    int z; 
    double w; 
    Point p; 
    }; 
}; 

int main(void) 
{ 
} 
बेशक

, प्लेसमेंट नया अभी भी जब एक vivifying इस्तेमाल किया जाना चाहिए कन्स्ट्रक्टर में शुरू किए गए दूसरे के अलावा संस्करण सदस्य।

+0

क्या होता है यदि 'फू' कन्स्ट्रक्टर परिभाषित किया गया है लेकिन उन सदस्यों के बीच (जिसे 'Foo(): p() {} ') के बजाय' Foo() {}' में से एक नहीं चुनता है? जीसीसी 5.1 और क्लैंग 3.6 किसी भी चेतावनी या त्रुटि के बिना कन्स्ट्रक्टर को संकलित करने के लिए बाहर निकलते हैं: http://melpon.org/wandbox/permlink/gLkD49UOrrGhFUJc हालांकि, यह स्पष्ट नहीं है कि उस मामले के बारे में मानक क्या कहता है। – dkim

+0

@dkim: मुझे पूरा यकीन है कि एक ही राज्य में सभी प्रकार के सदस्यों को छोड़ देता है, विशेष रूप से भंडारण प्राप्त किया जाता है लेकिन प्रारंभिकता नहीं की जाती है। धारा 3.8 (ऑब्जेक्ट लाइफटाइम) ऐसे राज्य में सदस्यों पर अनुमत संचालन निर्दिष्ट करता है। –

13

new (&p) Point() उदाहरण मानक प्लेसमेंट new ऑपरेटर (प्लेसमेंट नई अभिव्यक्ति के माध्यम से) के लिए एक कॉल है, इसलिए आपको <new> क्यों शामिल करने की आवश्यकता है। वह विशेष ऑपरेटर विशेष है कि मेमोरी आवंटित नहीं करता है, यह केवल आपके द्वारा पारित किए गए कार्यों को लौटाता है (इस मामले में यह &p पैरामीटर है)। अभिव्यक्ति का शुद्ध परिणाम यह है कि एक वस्तु का निर्माण किया गया है।

आप स्पष्ट नाशक कॉल के साथ इस वाक्य गठबंधन तो आप एक वस्तु के जीवन पर पूरा नियंत्रण हासिल कर सकते हैं:

// Let's assume storage_type is a type 
// that is appropriate for our purposes 
storage_type storage; 

std::string* p = new (&storage) std::string; 
// p now points to an std::string that resides in our storage 
// it was default constructed 

// *p can now be used like any other string 
*p = "foo"; 

// Needed to get around a quirk of the language 
using string_type = std::string; 

// We now explicitly destroy it: 
p->~string_type(); 
// Not possible: 
// p->~std::string(); 

// This did nothing to our storage however 
// We can even reuse it 
p = new (&storage) std::string("foo"); 

// Let's not forget to destroy our newest object 
p->~string_type(); 

जब और जहाँ आप का निर्माण और std::string सदस्य को नष्ट करना चाहिए (यह s कॉल) आपके Value कक्षा में s के लिए आपके उपयोग पैटर्न पर निर्भर करता है। इस कम से कम उदाहरण में आप विशेष के सदस्यों में निर्माण कभी नहीं (और इसलिए संहार) यह:

Value v; 
new (&v.s) std::string("foo"); 
something_taking_a_string(v.s); 
using string_type = std::string; 
v.s.~string_type(); 

आपने गौर किया हो सकता है, मैं विकलांग:

struct Value { 
    Value() {} 

    Value(Value const&) = delete; 
    Value& operator=(Value const&) = delete; 

    Value(Value&&) = delete; 
    Value& operator=(Value&&) = delete; 

    ~Value() {} 

    uint64_t lastUpdated; 

    union { 
     uint64_t ui; 
     int64_t i; 
     float f; 
     bool b; 
     std::string s; 
    }; 
}; 

निम्नलिखित इस प्रकार Value का एक मान्य इस्तेमाल होता है प्रतिलिपि बनाना और Value स्थानांतरित करना। इसका कारण यह है कि हम संघ के उपयुक्त सक्रिय सदस्य की प्रतिलिपि या स्थानांतरित नहीं कर सकते हैं, यह जानने के बिना कि यह कौन सा सक्रिय है, यदि कोई हो।

+5

* "// भाषा के चारों ओर घूमने की आवश्यकता है" * - वास्तव में, यह गलत है - यदि आप इसे सही करते हैं तो आपको विनाशक को सीधे कॉल कर सकते हैं (दायरे को सही ढंग से हल करने की आवश्यकता है): 'p-> std :: स्ट्रिंग :: ~ स्ट्रिंग(); '। अधिक पठनीय, यद्यपि? खैर, निश्चित रूप से अधिक जटिल लग रहा है, लेकिन एक प्रसिद्ध डेटा प्रकार का उपयोग करता है, जबकि उपरोक्त समाधान अधिक कॉम्पैक्ट है ('उपयोग' के लिए अतिरिक्त कोड लाइन के अलावा), लेकिन शायद ही कभी ज्ञात उपनाम प्रस्तुत करता है। निश्चित रूप से व्यक्तिगत स्वाद का विषय (जैसा कि मेरे विषय में है, मैं प्रसिद्ध डेटा प्रकार के लिए वोट दूंगा ...)। – Aconcagua