स्थिर वर्ग के सदस्य का अपरिभाषित संदर्भ


201

क्या कोई समझा सकता है कि निम्नलिखित कोड संकलन क्यों नहीं करेगा? कम से कम g ++ 4.2.4 पर।

और अधिक दिलचस्प, यह क्यों संकलित करेगा जब मैंने प्रोफ़ाइल को इंट में डाल दिया?

#include <vector>

class Foo {  
public:  
    static const int MEMBER = 1;  
};

int main(){  
    vector<int> v;  
    v.push_back( Foo::MEMBER );       // undefined reference to `Foo::MEMBER'
    v.push_back( (int) Foo::MEMBER ); // OK  
    return 0;
}

मैंने <pre> <code> </ code> </ pre> का उपयोग करने के बजाय चार स्थानों द्वारा कोड को इंडेंट करने के लिए प्रश्न संपादित किया। इसका मतलब है कि कोष्ठक को HTML के रूप में व्याख्यायित नहीं किया गया है।
स्टीव जेसप

stackoverflow.com/questions/16284629/… आप इस प्रश्न का उल्लेख कर सकते हैं।
तोबा इकबाल

जवाबों:


199

आपको वास्तव में कहीं न कहीं (कक्षा की परिभाषा के बाद) स्थैतिक सदस्य को परिभाषित करने की आवश्यकता है। इसे इस्तेमाल करे:

class Foo { /* ... */ };

const int Foo::MEMBER;

int main() { /* ... */ }

जिसे अपरिभाषित संदर्भ से छुटकारा मिलना चाहिए।


3
अच्छा बिंदु, इनलाइन स्टेटिक कास्ट पूर्णांक इनिशियलाइज़ेशन एक स्कोप्ड पूर्णांक स्थिरांक बनाता है जिसे आप का पता नहीं ले सकते हैं, और वेक्टर एक संदर्भ परम लेता है।
इवान टेरान

10
यह उत्तर केवल प्रश्न के पहले भाग को संबोधित करता है। दूसरा भाग ज्यादा दिलचस्प है: बाहरी घोषणा की आवश्यकता के बिना एनओपी कास्ट को जोड़ने से यह काम क्यों करता है?
नोबार

32
मैंने अभी यह जानने में अच्छा समय बिताया है कि यदि वर्ग परिभाषा हेडर फ़ाइल में है, तो स्थैतिक चर का आवंटन शीर्षक फ़ाइल में होना चाहिए, न कि कार्यान्वयन फ़ाइल में।
शानेट

1
@ शशांक: बहुत अच्छी बात - मुझे अपने उत्तर में इसका उल्लेख करना चाहिए था!
ड्रू हॉल

लेकिन अगर मैं इसे कॉन्स्टेबल घोषित करता हूं, तो क्या मेरे लिए उस वेरिएबल की वैल्यू को बदलना संभव नहीं है?
नम्रता

78

नई C ++ सुविधाओं की एक दिलचस्प झड़प और आप जो करने की कोशिश कर रहे हैं, उसके कारण समस्या आती है। सबसे पहले, push_backहस्ताक्षर पर एक नज़र डालते हैं :

void push_back(const T&)

यह एक प्रकार की वस्तु के संदर्भ की अपेक्षा कर रहा है T। आरंभीकरण की पुरानी प्रणाली के तहत, ऐसा सदस्य मौजूद है। उदाहरण के लिए, निम्न कोड ठीक संकलित करता है:

#include <vector>

class Foo {
public:
    static const int MEMBER;
};

const int Foo::MEMBER = 1; 

int main(){
    std::vector<int> v;
    v.push_back( Foo::MEMBER );       // undefined reference to `Foo::MEMBER'
    v.push_back( (int) Foo::MEMBER ); // OK  
    return 0;
}

ऐसा इसलिए है क्योंकि कहीं न कहीं एक वास्तविक वस्तु है जो उस मूल्य को उसमें संग्रहीत करती है। यदि, हालांकि, आप स्थिर कॉन्स्टेबल सदस्यों को निर्दिष्ट करने के नए तरीके पर स्विच करते हैं, जैसे कि आपके पास ऊपर है, Foo::MEMBERअब कोई वस्तु नहीं है। यह एक स्थिर, कुछ हद तक समान है:

#define MEMBER 1

लेकिन एक प्रीप्रोसेसर मैक्रो के सिरदर्द के बिना (और प्रकार की सुरक्षा के साथ)। इसका मतलब है कि वेक्टर, जो एक संदर्भ की उम्मीद कर रहा है, एक नहीं मिल सकता है।


2
धन्यवाद, कि मदद ... कि stackoverflow.com/questions/1995113/strangest-language-feature के लिए अर्हता प्राप्त कर सकता है अगर यह पहले से ही वहाँ नहीं है ...
आंद्रे होल्जनर

1
यह भी ध्यान देने योग्य है कि MSVC बिना किसी शिकायत के नॉन-कास्ट संस्करण स्वीकार करता है।
पोर्स

4
-1: यह बस सच नहीं है। आप अभी भी स्थैतिक सदस्यों को प्रारंभिक इनलाइन को परिभाषित करने वाले हैं, जब वे कहीं - कहीं ओडीआर-इस्तेमाल किए जाते हैं । कंपाइलर ऑप्टिमाइज़ेशन से आपके लिंकर की त्रुटि से छुटकारा मिल सकता है जो कि परिवर्तित नहीं होता है। इस स्थिति में आपका lvalue-to-rvalue रूपांतरण ( (int)कलाकारों के लिए धन्यवाद ) निरंतर की दृश्यता के साथ अनुवाद इकाई में होता है, और यह Foo::MEMBERअब ओआरआर-उपयोग नहीं है । यह पहले फ़ंक्शन कॉल के विपरीत है, जहां एक संदर्भ को चारों ओर पारित किया जाता है और कहीं और मूल्यांकन किया जाता है।
कक्षा

किस बारे में void push_back( const T& value );? const&है rvalues ​​के साथ बांध सकते हैं।
कोस्टा

59

यदि किसी तरह की जरूरत है तो C ++ मानक को आपके स्थिर कॉन्स्टेबल सदस्य के लिए परिभाषा की आवश्यकता होती है।

परिभाषा की आवश्यकता है, उदाहरण के लिए यदि इसका उपयोग किया जाता है। push_backकॉन्स्ट सन्दर्भ द्वारा इसका पैरामीटर लेता है, और इसलिए कड़ाई से संकलक को आपके सदस्य के पते की आवश्यकता होती है और आपको इसे नाम स्थान में परिभाषित करने की आवश्यकता होती है।

जब आप स्पष्ट रूप से निरंतर कास्ट करते हैं, तो आप एक अस्थायी बना रहे हैं और यह यह अस्थायी है जो संदर्भ (मानक में विशेष नियमों के तहत) के लिए बाध्य है।

यह वास्तव में एक दिलचस्प मामला है, और मुझे वास्तव में लगता है कि यह एक मुद्दा उठाने के लायक है, ताकि आपके निरंतर सदस्य के लिए समान व्यवहार करने के लिए एसटीडी को बदल दिया जाए!

हालाँकि, एक अजीब तरह के तरीके में इसे unary '+' ऑपरेटर के वैध उपयोग के रूप में देखा जा सकता है। मूल रूप से एक परिणाम है, unary +और इसलिए संदर्भों को बाध्य करने के नियम को लागू करने के नियम लागू होते हैं और हम अपने स्थिर सदस्य के पते का उपयोग नहीं करते हैं:

v.push_back( +Foo::MEMBER );

3
+1। हां यह निश्चित रूप से अजीब है कि टाइप टी के ऑब्जेक्ट एक्स के लिए, अभिव्यक्ति "(टी) एक्स" का उपयोग एक कॉन्स्ट रिफ को बांधने के लिए किया जा सकता है जबकि सादे "एक्स" नहीं। मुझे "unary +" के बारे में आपका अवलोकन बहुत पसंद है! किसने सोचा होगा कि गरीब थोड़ा "unary +" वास्तव में एक उपयोग था ... :)
j_random_hacker

3
सामान्य मामले के बारे में सोचकर ... क्या C ++ में किसी अन्य प्रकार की वस्तु है जिसके पास यह संपत्ति है कि इसे (1) का उपयोग केवल एक लैवल्यू के रूप में किया जा सकता है यदि इसे परिभाषित किया गया है, लेकिन (2) को बिना रिवैल्यू में परिवर्तित किया जा सकता है। परिभाषित?
j_random_hacker

अच्छा सवाल है, और कम से कम इस समय मैं किसी अन्य उदाहरण के बारे में नहीं सोच सकता। यह शायद केवल यहाँ है क्योंकि समिति ज्यादातर मौजूदा सिंटैक्स का पुन: उपयोग कर रही थी।
रिचर्ड कॉर्डन

@ रीचर्ड कॉर्डेन: यूनरी + ने कैसे हल किया?
रक्त-हैजर्ड

1
@ रक्त- HaZaRd: इससे पहले कि केवल push_backएक अधिभार का संदर्भ था const &। सदस्य का सीधे उपयोग करने से सदस्य संदर्भ के लिए बाध्य हो जाता है, जिसके लिए उसे एक पता होना चाहिए। हालाँकि, +सदस्य के मान के साथ एक अस्थायी बनाता है। संदर्भ तब सदस्य को एक पते की आवश्यकता के बजाय उस अस्थायी को बांधता है।
रिचर्ड कॉर्डन

10

Aaa.h

class Aaa {

protected:

    static Aaa *defaultAaa;

};

Aaa.cpp

// You must define an actual variable in your program for the static members of the classes

static Aaa *Aaa::defaultAaa;

1

पता नहीं क्यों कलाकार काम करता है, लेकिन फू :: प्रोफ़ाइल तब तक आवंटित नहीं किया जाता है जब तक पहली बार फू लोड नहीं किया जाता है, और जब से आप इसे लोड नहीं कर रहे हैं, यह कभी भी आवंटित नहीं किया जाता है। यदि आपके पास कहीं फू का संदर्भ था, तो यह शायद काम करेगा।


मुझे लगता है कि आप अपने स्वयं के प्रश्न का उत्तर दे रहे हैं: कलाकार काम करता है क्योंकि यह एक (अस्थायी) संदर्भ बनाता है।
Jaap Versteegh

1

C ++ 11 के साथ, उपरोक्त मूल प्रकारों के लिए संभव होगा

class Foo {
public:  
  static constexpr int MEMBER = 1;  
};

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


यह मेरे लिए आवश्यक हो गया, क्योंकि "स्टेटिक कॉन्स्टेंस इंट प्रोफाइल = 1;" स्विच में प्रोफ़ाइल का उपयोग करने की आवश्यकता है, जबकि वैक्टर में इसका उपयोग करने के लिए बाहरी घोषणा की आवश्यकता होती है, और आप दोनों एक ही समय में नहीं हो सकते। लेकिन जो अभिव्यक्ति आप यहां देते हैं, वह दोनों के लिए काम करती है, कम से कम मेरे कंपाइलर के साथ।
बेन किसान

0

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

या कम से कम मेरे सहकर्मी जिन्होंने इसका समाधान किया, उन्होंने ऐसा कहा।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.