2012-01-03 5 views
19

पर विचार करें परस्पर संदर्भित प्रकार के निम्नलिखित जोड़ी:आरंभ किया जा रहा परस्पर-संदर्भित वस्तुओं

struct A; 
struct B { A& a; }; 
struct A { B& b; }; 

यह जीसीसी में कुल आरंभीकरण के साथ प्रारंभ किया जा सकता है, बजना, इंटेल, MSVC, लेकिन नहीं SunPro कि उपयोगकर्ता परिभाषित ctors का कहना है जो आवश्यक हैं।

struct {A first; B second;} pair = {pair.second, pair.first}; 

क्या यह प्रारंभिक कानूनी है?

थोड़ा और अधिक व्यापक प्रदर्शन: http://ideone.com/P4XFw

अब, सूर्य की चेतावनी heeding, क्या उपयोगकर्ता परिभाषित कंस्ट्रक्टर्स के साथ कक्षाओं के बारे में? निम्नलिखित जीसीसी, क्लैंग, इंटेल, सनप्रो, और एमएसवीसी में काम करता है, लेकिन क्या यह कानूनी है?

struct A; 
struct B { A& ref; B(A& a) : ref(a) {} }; 
struct A { B& ref; A(B& b) : ref(b) {} }; 

struct {B first; A second;} pair = {pair.second, pair.first}; 

डेमो: http://ideone.com/QQEpA

और अंत में, क्या हुआ अगर कंटेनर या तो तुच्छ नहीं है, उदाहरण के लिए (जी ++, इंटेल, बजना (चेतावनी के साथ) में काम करता है, लेकिन नहीं MSVC ("युग्मित" प्रारंभकर्ता में अज्ञात) या SunPro ("जोड़ी नहीं एक संरचना है")

std::pair<A, B> pair(pair.second, pair.first); 
मैं क्या देख सकते हैं, §3.8[basic.life]/6 मना करता है से

इससे पहले कि जीवन शुरू होता है एक गैर स्थिर डेटा सदस्य के लिए उपयोग, लेकिन pair.second "पहुँच" के लिए दूसरा? अगर ऐसा है, तो सभी तीन initializations अवैध हैं lvalue मूल्यांकन है? इसके अलावा, §8.3.2[dcl.ref]/5 कहते हैं, "संदर्भ एक का उल्लेख करने के प्रारंभ की जाएगी वैध ऑब्जेक्ट "जो शायद तीनों को भी अवैध बनाता है, लेकिन शायद मुझे कुछ याद आ रहा है और कंपेलर इसे किसी कारण से स्वीकार करते हैं।

पीएस: मुझे एहसास है कि ये कक्षाएं किसी भी तरह से व्यावहारिक नहीं हैं, इसलिए ला nguage-वकील टैग। संबंधित और मामूली रूप से अधिक व्यावहारिक वर्ष यहाँ चर्चा: Circular reference in C++ without pointers

+2

मेरी आंत महसूस यह है कि आपके कुल निर्माण सही हैं (हालांकि मैं इसे अभी साबित नहीं कर सकता), जबकि 'std :: pair' के साथ गैर-समेकित संस्करण निश्चित रूप से आपके द्वारा बताए गए कारणों के लिए अनुमति नहीं है। आप एक सरल सवाल पूछ सकते हैं: 'स्ट्रक्चर फू {फू & r; फू (फू एंड एफ): आर (एफ) {}} एक्स (एक्स); ' –

+0

क्या आप इसे पॉइंटर्स के साथ काम करने के लिए प्राप्त कर सकते हैं? –

+0

दूसरा उदाहरण कानूनी नहीं है, जहां तक ​​मैं कह सकता हूं, केवल समेकित (जो उपयोगकर्ता द्वारा घोषित कन्स्ट्रक्टर नहीं हो सकता है) में ब्रेस-प्रारंभकर्ता हो सकते हैं: '§8.5 [dcl।init]/14' –

उत्तर

1

यह एक पहली बार में मेरे मन warping गया था, लेकिन मुझे लगता है मैं इसे अब मिल गया लगता है। 1 99 8 के मानक के 12.6.2.5 के अनुसार, सी ++ गारंटी देता है कि कक्षा के सदस्यों को कक्षा में घोषित क्रम में आरंभ किया जाता है, और सभी सदस्यों के प्रारंभ होने के बाद निर्माता निकाय को निष्पादित किया जाता है। इसका मतलब है कि अभिव्यक्ति

struct A; 
struct B { A& a; }; 
struct A { B& b; }; 
struct {A first; B second;} pair = {pair.second, pair.first}; 

के बाद से जोड़ी एक ऑटो (स्थानीय, ढेर) चर रहा है समझ में आता है, इसलिए अपने रिश्तेदार का पता और सदस्यों के पते संकलक के लिए जाना जाता है, और वहाँ पहले और दूसरे के लिए कोई निर्माता ये हैं।

क्यों दो स्थितियों कोड मतलब ऊपर समझ में आता है: जब first, प्रकार A की,, first के डेटा सदस्य b सेट किया गया है (pair के किसी भी अन्य डेटा सदस्य से पहले) का निर्माण किया है को संदर्भित करने के pair.second, जिनमें से पता संकलक के लिए जाना जाता है क्योंकि यह एक स्टैक वैरिएबल है (प्रोग्राम में इसके लिए पहले से मौजूद स्थान, AFAIU) है। ध्यान दें कि pair.second एक वस्तु के रूप में, यानी स्मृति खंड, प्रारंभ नहीं किया गया है (कचरा होता है), लेकिन यह सच है कि कि कचरे के पते के संकलन समय पर जाना जाता है और संदर्भ सेट करने के लिए इस्तेमाल किया जा सकता नहीं बदलता है। चूंकि A में कोई कन्स्ट्रक्टर नहीं है, इसलिए यह b के साथ कुछ भी करने का प्रयास नहीं कर सकता है, इसलिए व्यवहार अच्छी तरह से परिभाषित किया गया है। a संदर्भ pair.first, प्रकार A की है जो अपने डेटा सदस्य, और pair.first पता संकलक से जाना जाता है: एक बार first शुरू कर दिया गया है, यह second की बारी है, और एक ही है।

पतों संकलक से जाना जाता है नहीं कर रहे थे (जैसे कि नए ऑपरेटर के माध्यम से ढेर स्मृति का उपयोग क्योंकि), तो त्रुटि को संकलन, या यदि नहीं, अपरिभाषित व्यवहार किया जाना चाहिए। हालांकि नियुक्ति नए ऑपरेटर का विवेकपूर्ण उपयोग यह काम करने के लिए, के बाद से तो फिर दोनों first और second के पते समय first आरंभ नहीं हो जाता से जाना जा सकता है की अनुमति दे सकता।

अब

बदलाव के लिए:

struct A; 
struct B { A& ref; B(A& a) : ref(a) {} }; 
struct A { B& ref; A(B& b) : ref(b) {} }; 
struct {B first; A second;} pair = {pair.second, pair.first}; 

पहले कोड उदाहरण से फर्क सिर्फ इतना है कि B निर्माता स्पष्ट रूप से परिभाषित किया गया है है, लेकिन विधानसभा कोड निश्चित रूप से समान है के रूप में वहाँ निर्माता शरीर में कोई कोड है। तो यदि पहला कोड नमूना काम करता है, तो दूसरा भी चाहिए।

हालांकि

, अगर वहाँ बी के निर्माता शरीर है, जो कुछ (pair.second) के लिए एक संदर्भ हो रही है में कोड है कि अभी तक प्रारंभ नहीं किया गया है (लेकिन जिसके लिए पता परिभाषित किया और जाना जाता है), और है कि कोड a का उपयोग करता है, अच्छी तरह से आप परेशानी की तलाश में हैं। यदि आप भाग्यशाली हैं तो आपको एक क्रैश मिलेगा, लेकिन a पर लिखना शायद चुपचाप विफल हो जाएगा क्योंकि A कन्स्ट्रक्टर को अंततः कॉल करने पर मूल्यों को ओवरराइट किया जाता है।

1

दृश्य संदर्भ का संकलक बिंदु से की और कुछ नहीं लेकिन स्थिरांक संकेत दिए गए हैं। संकेत के साथ अपने उदाहरण पुनर्लेखन और यह स्पष्ट हो जाता है कि कैसे और क्यों यह काम करता है:

struct A; 
struct B { A* a; }; 
struct A { B* b; }; 
struct {A first; B second;} pair = {&(pair.second), &(pair.first)}; //parentheses for clarity 

रूप Schollii लिखा है: स्मृति पहले से आवंटित किया जाता है, इस प्रकार पता। संदर्भ/पॉइंटर्स के कारण कोई पहुंच नहीं है और न ही मूल्यांकन है। यह केवल "दूसरा" और "पहला", सरल सूचक अंकगणित के पते ले रहा है।

मैं कैसे किसी भी ऑपरेटर के अलावा अन्य जगह में संदर्भों का उपयोग के बारे में शेख़ी सकता भाषा दुरुपयोग है, लेकिन मुझे लगता है कि इस उदाहरण मुद्दा काफी अच्छी तरह से :)

(अब से पर मैं मैन्युअल रूप से सभी ctors बारे में प्रकाश डाला गया है। आपका संकलक या आप के लिए पूर्ण रूप से अपने ऐसा कर सकते हैं हो सकता है नहीं) नई उपयोग करके देखें:।

struct A; 
struct B { A& a; B(A& arg):a(arg){;} }; 
struct A { B& b; A(B& arg):b(arg){;} }; 
typedef struct PAIR{A first; B second; PAIR(B& argB, A& argA):first(argB),second(argA){;}} *PPAIR, *const CPPAIR; 
PPAIR pPair = NULL;// just to clean garbage or 0xCDCD 
pPair = new PAIR(pPair->second, pPair->first); 

अब यह निष्पादन के आदेश पर निर्भर करता है। यदि असाइनमेंट आखिरी (सीटीओआर के बाद) किया जाता है तो दूसरा पी 0x0000 और पहले .ref को इंगित करेगा। 0x0004।
वास्तव में, http://codepad.org/yp911ug6 यहाँ यह ctors जो पिछले चलाए जा रहे हैं (सबसे समझ में आता है!), इसलिए सब कुछ काम करता है (भले ही ऐसा लगता है ऐसा नहीं होना चाहिए) है।

, हालांकि टेम्पलेट्स के बारे में बात नहीं कर सकते।

लेकिन अपने सवाल था "यह कानूनी है?"। कोई कानून इसे मना नहीं करता है।
क्या यह काम करेगा? खैर, मैं इस बारे में कोई बयान देने के लिए पर्याप्त संकलक निर्माताओं पर भरोसा नहीं करता हूं।