कक्षा की परिभाषा में स्थिर कास्ट पूर्णांक सदस्यों को परिभाषित करना


109

मेरी समझ यह है कि C ++ एक स्थिर प्रकार के सदस्यों को वर्ग के अंदर परिभाषित करने की अनुमति देता है जब तक कि यह पूर्णांक प्रकार है।

फिर, निम्न कोड मुझे लिंकर त्रुटि क्यों देता है?

#include <algorithm>
#include <iostream>

class test
{
public:
    static const int N = 10;
};

int main()
{
    std::cout << test::N << "\n";
    std::min(9, test::N);
}

मुझे जो त्रुटि मिलती है वह है:

test.cpp:(.text+0x130): undefined reference to `test::N'
collect2: ld returned 1 exit status

दिलचस्प बात यह है कि अगर मैं std :: min के लिए कॉल को कमेंट करता हूं, तो कोड संकलन और लिंक को ठीक करता है (भले ही परीक्षण :: N को पिछली पंक्ति में भी संदर्भित किया जाता है)।

किसी भी विचार के रूप में क्या हो रहा है?

मेरा कंपाइलर लिनक्स पर gcc 4.4 है।


3
विजुअल स्टूडियो 2010 पर ठीक काम करता है।
पपी

4
इस सटीक त्रुटि को gcc.gnu.org/wiki/…
जोनाथन

के विशेष मामले में char, आप इसके बजाय इसे परिभाषित कर सकते हैं constexpr static const char &N = "n"[0];। ध्यान दें &। मुझे लगता है कि यह काम करता है क्योंकि शाब्दिक तार स्वचालित रूप से परिभाषित होते हैं। मैं इसके बारे में चिंतित हूं, हालांकि - यह विभिन्न अनुवाद इकाइयों के बीच एक हेडर फ़ाइल में अजीब व्यवहार कर सकता है, क्योंकि स्ट्रिंग संभवतः कई अलग-अलग पते पर होगी।
हारून मैकडैड

1
यह प्रश्न इस बात का प्रकटीकरण है कि C ++ का उत्तर "स्थिरांक के लिए #defines का उपयोग न करें" फिर भी कितना खराब है।
जोहान्स ओवरमैन

1
@JohannesOvermann इस संबंध में, मैं C ++ 17 के बाद से वैश्विक चर के लिए इनलाइन के उपयोग का उल्लेख करना चाहता हूं inline const int N = 10, जो कि मेरे ज्ञान में अभी भी लिंकर द्वारा परिभाषित भंडारण कहीं है। क्लास इनफ़्लेमशन टेस्ट के अंदर स्टेटिक वेरिएबल परिभाषा प्रदान करने के लिए कीवर्ड इनलाइन का भी इस्तेमाल किया जा सकता है ।
वर्मर

जवाबों:


72

मेरी समझ यह है कि C ++ एक स्थिर प्रकार के सदस्यों को वर्ग के अंदर परिभाषित करने की अनुमति देता है जब तक कि यह पूर्णांक प्रकार है।

आप सही हैं। आपको वर्ग घोषणा में स्थिर कॉन्स्ट इंटीग्रल्स को इनिशियलाइज़ करने की अनुमति है लेकिन यह कोई परिभाषा नहीं है।

http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?topic=/com.ibm.xlcpp8a.doc/language/ref/cplr038.htm

दिलचस्प बात यह है कि अगर मैं std :: min के लिए कॉल को कमेंट करता हूं, तो कोड संकलन और लिंक को ठीक करता है (भले ही परीक्षण :: N को पिछली पंक्ति में भी संदर्भित किया जाता है)।

किसी भी विचार के रूप में क्या हो रहा है?

std :: min, const रेफ़रेंस द्वारा इसके पैरामीटर लेता है। यदि यह उन्हें मूल्य से लेता है, तो आपको यह समस्या नहीं होगी, लेकिन जब से आपको एक संदर्भ की आवश्यकता होती है, तो आपको एक परिभाषा की आवश्यकता होती है।

यहाँ अध्याय / कविता है:

9.4.2 / 4 - यदि कोई staticडेटा सदस्य constअभिन्न या constगणना प्रकार का है, तो कक्षा परिभाषा में इसकी घोषणा एक निरंतर-प्रारंभक निर्दिष्ट कर सकती है जो एक अभिन्न स्थिर अभिव्यक्ति (5.19) होगी। उस मामले में, सदस्य अभिन्न निरंतर अभिव्यक्तियों में प्रकट हो सकता है। यदि प्रोग्राम में उपयोग किया जाता है तो सदस्य को अभी भी नाम स्थान के दायरे में परिभाषित किया जाएगा और नेमस्पेस गुंजाइश परिभाषा में इनिशियलाइज़र नहीं होगा

संभावित वर्कअराउंड के लिए चू का उत्तर देखें।


मैं देखता हूं, यह दिलचस्प है। उस मामले में, घोषणा के बिंदु पर मूल्य प्रदान करने और परिभाषा के बिंदु पर मूल्य प्रदान करने के बीच क्या अंतर है? कौन सा अनुशंसित है?
HighCommander4

ठीक है, मेरा मानना ​​है कि आप एक परिभाषा के बिना इतने लंबे समय तक दूर रह सकते हैं जब तक कि आप वास्तव में चर का "उपयोग" न करें। यदि आप इसे निरंतर अभिव्यक्ति के भाग के रूप में उपयोग करते हैं तो चर का उपयोग कभी नहीं किया जाता है। अन्यथा हेडर में मूल्य देखने में सक्षम होने के अलावा एक बहुत बड़ा अंतर नहीं लगता है - जो आप चाहते हैं या नहीं हो सकता है।
एडवर्ड स्ट्रेंज

2
Terse जवाब स्थिर const x = 1 है; एक प्रतिद्वंद्विता है, लेकिन एक अंतराल नहीं है। मूल्य संकलन समय पर एक स्थिरांक के रूप में उपलब्ध है (आप इसके साथ एक सरणी को आयाम दे सकते हैं) स्थैतिक कांस्ट y; [कोई आरंभिक] एक सीपीपी फ़ाइल में परिभाषित नहीं किया जाना चाहिए और या तो एक लकीर या एक अंतराल के रूप में इस्तेमाल किया जा सकता है।
डेल विल्सन

2
अच्छा होगा यदि वे इसका विस्तार / सुधार कर सकें। प्रारंभिक-लेकिन-नहीं-परिभाषित वस्तुओं को, मेरी राय में, शाब्दिक के समान माना जाना चाहिए। उदाहरण के लिए, हमें शाब्दिक रूप 5से बांधने की अनुमति है const int&। तो क्यों नहीं ओपी के test::Nअनुरूप शाब्दिक व्यवहार करें?
एरोन मैकडैड

दिलचस्प व्याख्या, धन्यवाद! इसका मतलब यह है कि C ++ स्टैटिक कॉन्स्टेंस इंट में अभी भी पूर्णांक #defines के लिए कोई प्रतिस्थापन नहीं है। Enum हमेशा केवल हस्ताक्षरित int होता है, इसलिए किसी को अलग-अलग स्थिरांक के लिए enum कक्षाओं का उपयोग करना पड़ता है। यह मेरे लिए काफी स्पष्ट होगा कि मैं निरंतर घोषणा को निरंतर करता रहूं और मूल्यों को एक शाब्दिक निरंतर के रूप में जानता हूं जिस तरह से यह समस्याओं के बिना संकलित होगा। C ++ के लिए लंबा रास्ता तय करना है ...
जोहान्स ओवरमैन

51

उनके C ++ FAQ में Bjarne Stroustrup का उदाहरण बताता है कि आप सही हैं, और केवल एक परिभाषा की आवश्यकता है यदि आप पते को लेते हैं।

class AE {
    // ...
public:
    static const int c6 = 7;
    static const int c7 = 31;
};

const int AE::c7;   // definition

int f()
{
    const int* p1 = &AE::c6;    // error: c6 not an lvalue
    const int* p2 = &AE::c7;    // ok
    // ...
}

वह कहते हैं, "आप स्थैतिक सदस्य का पता ले सकते हैं यदि (और केवल अगर) तो इसकी एक आउट-ऑफ-क्लास परिभाषा है" । जो यह सुझाव देता है कि यह अन्यथा काम करेगा। हो सकता है कि आपका न्यूनतम कार्य किसी तरह से दृश्यों के पीछे के पते को आमंत्रित करता हो।


2
std::minसंदर्भ द्वारा इसके मापदंडों को लेता है, यही कारण है कि एक परिभाषा की आवश्यकता है।
Rakete1111

मैं परिभाषा कैसे लिखूंगा अगर AE एक टेम्पलेट वर्ग है AE <वर्ग T> और c7 एक int नहीं है लेकिन T :: size_type है? मैंने हेडर में "-1" को इनिशियलाइज़ किया है लेकिन क्लैग अपरिभाषित मान कहता है और मुझे नहीं पता कि परिभाषा कैसे लिखनी है।
फेबियन

@ फैबियन मैं यात्रा कर रहा हूं और एक फोन पर और थोड़ा व्यस्त हूं ... लेकिन मुझे लगता है कि आपकी टिप्पणी से ऐसा लगता है कि यह एक नए प्रश्न के रूप में लिखा जाएगा। एक ऊपर लिखें MCVE त्रुटि आपको मिल भी शामिल है, यह भी हो सकता है क्या जीसीसी कहते हैं में फेंक देते हैं। मुझे यकीन है कि लोग आपको बताएंगे कि जल्दी क्या है।
HostileFork का कहना है कि

@HostileFork: MCVE लिखते समय, आप कभी-कभी अपने आप को हल कर लेते हैं। मेरे मामले के लिए उत्तर है template<class K, class V, class C> const typename AE<K,V,C>::KeyContainer::size_type AE<K,V,C>::c7;जहां KeyContainer std का एक टाइप किया जाता है :: वेक्टर <K>। सभी टेम्प्लेट मापदंडों को सूचीबद्ध करना चाहिए और टाइपनाम लिखना होगा क्योंकि यह एक आश्रित प्रकार है। शायद किसी को यह टिप्पणी उपयोगी लगेगी। हालांकि, अब मुझे आश्चर्य है कि इसे DLL में कैसे निर्यात किया जाए क्योंकि टेम्प्लेट क्लास एक हेडर में है। क्या मुझे c7 निर्यात करने की आवश्यकता है ???
फैबियन

24

ऐसा करने का एक और तरीका है, पूर्णांक प्रकारों के लिए वैसे भी, स्थिरांक को कक्षा में शत्रु के रूप में परिभाषित करना है:

class test
{
public:
    enum { N = 10 };
};

2
और यह शायद समस्या को हल करेगा। जब N का उपयोग मिनट () के लिए एक पैरामीटर के रूप में किया जाता है तो यह एक अस्थायी रूप से निर्मित होने के बजाय एक कथित रूप से विद्यमान चर को संदर्भित करने का प्रयास करेगा।
एडवर्ड स्ट्रेंज

इसका यह फायदा था कि इसे निजी बनाया जा सकता है।
अगस्टिनो

11

सिर्फ इंट का नहीं। लेकिन आप वर्ग घोषणा में मूल्य को परिभाषित नहीं कर सकते। यदि आपके पास है:

class classname
{
    public:
       static int const N;
}

.ह फ़ाइल में तब आपके पास होना चाहिए:

int const classname::N = 10;

.cpp फ़ाइल में।


2
मुझे पता है कि आप वर्ग घोषणा के अंदर किसी भी प्रकार का एक चर घोषित कर सकते हैं । मैंने कहा कि मुझे लगा कि स्थिर पूर्णांक स्थिरांक को वर्ग घोषणा के अंदर भी परिभाषित किया जा सकता है । क्या यह मामला नही है? यदि नहीं, तो ऐसा क्यों है कि संकलक उस पंक्ति में त्रुटि नहीं देता है जहां मैं इसे कक्षा के अंदर परिभाषित करने का प्रयास करता हूं? इसके अलावा, std :: cout लाइन लिंकर त्रुटि का कारण क्यों नहीं बनती, लेकिन std :: min line क्या करती है?
HighCommander4

नहीं, वर्ग घोषणा में स्थैतिक सदस्यों को परिभाषित नहीं कर सकता क्योंकि आरंभीकरण कोड का उत्सर्जन करता है। एक इनलाइन फ़ंक्शन के विपरीत जो कोड का भी उत्सर्जन करता है, एक स्थिर परिभाषा विश्व स्तर पर अद्वितीय है।
अमरदीप AC9MF

@ HighCommander4: आप static constवर्ग परिभाषा में अभिन्न सदस्य के लिए एक इनिशलाइज़र की आपूर्ति कर सकते हैं । लेकिन फिर भी यह उस सदस्य को परिभाषित नहीं करता है। विवरण के लिए नूह रॉबर्ट्स का जवाब देखें।
अंट

9

यहाँ समस्या के आसपास काम करने का एक और तरीका है:

std::min(9, int(test::N));

(मुझे लगता है कि क्रेजी एडी का जवाब सही ढंग से बताता है कि समस्या क्यों मौजूद है।)


5
या यहां तक ​​किstd::min(9, +test::N);
टॉमिलोव अनातोलि

हालांकि यह बड़ा सवाल है: क्या यह सब इष्टतम है? मैं आप लोगों के बारे में नहीं जानता, लेकिन परिभाषा को छोड़ देने का मेरा बड़ा आकर्षण यह है कि यह कांस्टेबल का उपयोग करने में कोई मेमोरी और कोई ओवरहेड नहीं लेना चाहिए।
Opux

6

C ++ 11 के अनुसार आप उपयोग कर सकते हैं:

static constexpr int N = 10;

यह सैद्धांतिक रूप से अभी भी आपको एक .cpp फ़ाइल में स्थिरांक को परिभाषित करने की आवश्यकता है, लेकिन जब तक आप इसका पता नहीं लेते हैं, Nयह बहुत कम संभावना नहीं है कि कोई संकलक कार्यान्वयन त्रुटि पैदा करेगा;)।


और क्या होगा यदि आपको उदाहरण में 'const int &' प्रकार के तर्क के रूप में मान पास करने की आवश्यकता है? :-)
वर्मर

यह ठीक काम करता है। आप इस तरह से एन को तत्काल नहीं कर रहे हैं , केवल एक अस्थायी के लिए एक कॉन्स्टेंस संदर्भ दे रहे हैं। wandbox.org/permlink/JWeyXwrVRvsn9cBj
कार्लो वुड

C ++ 17 हो सकता है, C ++ 14 नहीं, और यहां तक ​​कि G ++ 6.3 के पुराने संस्करणों में C ++ 17 और इससे कम नहीं, यह एक मानक चीज नहीं है। लेकिन इस उल्लेख के लिए धन्यवाद।
वर्मर

आह हाँ, आप सही हैं। मैंने wandbox पर c ++ 14 की कोशिश नहीं की। ओह ठीक है, वह हिस्सा है जहाँ मैंने कहा था "यह सैद्धांतिक रूप से अभी भी आपको निरंतर परिभाषित करने की आवश्यकता है"। तो, आप सही हैं कि यह 'मानक' नहीं है।
कार्लो वुड

3

C ++ एक वर्ग के अंदर स्थिर कॉन्स्टेबल सदस्यों को परिभाषित करने की अनुमति देता है

नहीं, 3.1 says2 कहता है:

एक घोषणा एक परिभाषा है जब तक कि यह फ़ंक्शन के शरीर (8.4) को निर्दिष्ट किए बिना एक फ़ंक्शन की घोषणा नहीं करता है, इसमें बाहरी विनिर्देशक (7.1.1) या एक लिंकेज-विनिर्देश (7.5) और न ही एक इनिशियलाइज़र और न ही एक कार्य है, यह एक स्थैतिक डेटा घोषित करता है एक वर्ग परिभाषा में सदस्य (9.4), यह एक वर्ग का नाम घोषणा (9.1) है, यह एक अपारदर्शी-एनम-घोषणा (7.2) है, या यह एक टाइप-डी घोषणा (7.1.3), एक प्रयोग-घोषणा (7.3) है। 3), या एक प्रयोग-निर्देश (7.3.4)।

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