स्थिर स्थिरांक चार का अपरिभाषित संदर्भ []


185

मैं static const charअपनी कक्षा में एक सरणी रखना चाहता हूं। जीसीसी ने शिकायत की और मुझे बताया कि मुझे इसका उपयोग करना चाहिए constexpr, हालांकि अब यह मुझे बता रहा है कि यह एक अपरिभाषित संदर्भ है। अगर मैं एरे को गैर-सदस्य बनाता हूं तो यह संकलन करता है। क्या हो रहा है?

// .hpp
struct foo {
  void bar();
  static constexpr char baz[] = "quz";
};

// .cpp
void foo::bar() {
  std::string str(baz); // undefined reference to baz
}

1
बस एक कूबड़, यह काम करता है अगर बाज उदाहरण के लिए int है? क्या आप इसे एक्सेस कर सकते हैं? यह एक बग भी हो सकता है।
विफल

1
@Pubby: प्रश्न: इसे किस अनुवाद इकाई में परिभाषित किया जाएगा? उत्तर: सब कुछ जिसमें हेडर शामिल है। समस्या: एक परिभाषा नियम का उल्लंघन करता है। अपवाद: संकलन-समय निरंतर अभिन्न हेडर में "आरंभिक" हो सकता है।
मूविंग डक

यह int@MooingDuck के रूप में ठीक संकलित करता है। यह गैर-सदस्य के रूप में ठीक काम करता है। क्या वह नियम का भी उल्लंघन नहीं करेगा?
Pubby

@ पबबी 8: intधोखा। जब तक C ++ 11 (संभव) के लिए नियम नहीं बदले जाते, एक गैर-सदस्य के रूप में, इसकी अनुमति नहीं दी जानी चाहिए
Mooing Duck

विचारों और विचारों को ध्यान में रखते हुए, इस प्रश्न के लिए अधिक विस्तृत उत्तर की आवश्यकता थी, जिसे मैंने नीचे जोड़ा।
शैफिक यघमौर

जवाबों:


188

अपनी cpp फ़ाइल में जोड़ें:

constexpr char foo::baz[];

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


70
यह अजीब लग रहा है ... क्योंकि यह कुछ जानकारी के साथ संकलक प्रदान करने के लिए प्रतीत नहीं होता है इससे पहले ...
दाखलताओं

32
यह तब और भी अजीब लगता है जब आपके पास .cpp फ़ाइल में अपनी कक्षा की घोषणा हो! आप वर्ग घोषणा में क्षेत्र को आरंभीकृत करते हैं, लेकिन आपको अभी भी वर्ग के नीचे कॉन्स्ट्रेप चार फू :: बाज [] लिखकर क्षेत्र को " घोषित " करने की आवश्यकता है । ऐसा लगता है कि कॉन्स्टैक्स का उपयोग करने वाले प्रोग्रामर एक अजीब टिप का पालन करके अपने कार्यक्रमों को संकलित कर सकते हैं: इसे फिर से घोषित करें।
लुकाज़ सेज़रविंस्की

5
@LukaszCzerwinski: आप जिस शब्द की तलाश कर रहे हैं, वह "परिभाषित" है।
केरेक एसबी

5
दाईं ओर, कोई नई जानकारी नहीं: घोषित करेंdecltype(foo::baz) constexpr foo::baz;
नहीं-एक-उपयोगकर्ता

6
अगर फू को टेम्पलेटाइज़ किया जाता है तो अभिव्यक्ति क्या दिखेगी? धन्यवाद।
Hei

80

C ++ 17 इनलाइन चर का परिचय देता है

C ++ 17 constexpr staticसदस्य चर के लिए इस समस्या को हल करता है, अगर यह ओड-यूज़ होती है तो आउट-ऑफ-लाइन परिभाषा की आवश्यकता होती है। प्री-सी ++ 17 विवरण के लिए इस उत्तर का दूसरा भाग देखें।

प्रस्ताव P0386 इनलाइन चर लागू करने की क्षमता का परिचय inlineविनिर्देशक चर करने के लिए। इस मामले के लिए विशेष रूप से constexprसंकेत मिलता है inlineस्थिर सदस्य चर के लिए। प्रस्ताव कहता है:

इनलाइन विनिर्देशक को चर के साथ-साथ कार्यों पर भी लागू किया जा सकता है। एक वैरिएबल घोषित इनलाइन में एक फ़ंक्शन के रूप में एक ही शब्दार्थ होता है इनलाइन घोषित: इसे परिभाषित किया जा सकता है, पहचान के अनुसार, एकाधिक अनुवाद इकाइयों में, हर अनुवाद इकाई में इसे परिभाषित किया जाना चाहिए, जिसमें इसका उपयोग किया जाता है, और कार्यक्रम का व्यवहार इस प्रकार है बिल्कुल एक चर है।

और संशोधित [basic.def] P2:

एक घोषणा एक परिभाषा है जब तक
...

  • यह एक वर्ग परिभाषा के बाहर एक स्थैतिक डेटा सदस्य की घोषणा करता है और वैरिएबल को क्लास के भीतर कॉन्स्टैक्सप्रो स्पेसिफ़ायर के साथ परिभाषित किया गया था (यह उपयोग पदावनत है; देखें [depr.static_constexpr]),

...

और [depr.static_constexpr] जोड़ें :

पूर्व C ++ अंतर्राष्ट्रीय मानकों के साथ अनुकूलता के लिए, एक स्थिर स्थैतिक डेटा सदस्य को बिना किसी इनिशियलाइज़र के साथ कक्षा से बाहर redeclared किया जा सकता है। यह उपयोग पदावनत है। [ उदाहरण:

struct A {
  static constexpr int n = 5;  // definition (declaration in C++ 2014)
};

constexpr int A::n;  // redundant declaration (definition in C++ 2014)

 - अंतिम उदाहरण]


C ++ 14 और पहले का

C ++ 03 में, हमें केवल const integrals या const enumeration type के लिए इन-क्लास इनिशियलाइज़र प्रदान करने की अनुमति दी गई थी , C ++ 11 में इसका उपयोग शाब्दिक प्रकारोंconstexpr तक बढ़ाया गया था ।

C ++ 11 में, हमें स्थैतिक constexprसदस्य के लिए नेमस्पेस स्कोप परिभाषा प्रदान करने की आवश्यकता नहीं है यदि यह odr-used नहीं है , तो हम इसे C ++ 11 मानक सेक्शन 9.4.2 [class.static.data] के मसौदे से देख सकते हैं, जो कहता है ( जोर मेरा आगे जा रहा है ):

[...] शाब्दिक प्रकार के एक स्थैतिक डेटा सदस्य को कॉन्स्ट्रेक्स स्पेसियर के साथ वर्ग परिभाषा में घोषित किया जा सकता है; यदि ऐसा है, तो इसकी घोषणा एक ब्रेस-या-सम-इनिशलाइज़र निर्दिष्ट करेगी जिसमें प्रत्येक इनिशियलाइज़र-क्लॉज जो एक असाइनमेंट-एक्सप्रेशन है, एक स्थिर अभिव्यक्ति है। [नोट: इन दोनों मामलों में, सदस्य निरंतर अभिव्यक्तियों में दिखाई दे सकता है। -एंड नोट] यदि प्रोग्राम में ओड-यूज़ेड (3.2) है तो सदस्य को नेमस्पेस स्कोप में परिभाषित किया जाएगा और नेमस्पेस स्कोप परिभाषा में इनिशियलाइज़र नहीं होगा।

तो फिर सवाल बन जाता है, क्या यहां baz ओआरआर का उपयोग किया जाता है:

std::string str(baz); 

और इसका उत्तर हां है , और इसलिए हमें नाम स्थान की परिभाषा की भी आवश्यकता है।

तो हम कैसे निर्धारित करते हैं कि एक चर का उपयोग किया जाता है ? अनुभाग में मूल C ++ 11 3.2 शब्दांकन [basic.def.odr] कहता है:

एक अभिव्यक्ति का मूल्यांकन संभावित रूप से तब तक किया जाता है जब तक कि यह एक अविकसित ऑपरेंड (क्लॉज 5) या उसके उपपरिवर्तन न हो। एक चर जिसका नाम एक संभावित मूल्यांकन अभिव्यक्ति के रूप में प्रकट होता है, जब तक कि यह एक ऐसी वस्तु हो, जब तक कि यह एक ऐसी वस्तु न हो जो एक स्थिर अभिव्यक्ति (5.19) में प्रकट होने के लिए आवश्यकताओं को संतुष्ट करती है और लैवल्यू-टू-रेवल्यू रूपांतरण (4.1) को तुरंत लागू किया जाता है

तो bazएक निरंतर अभिव्यक्ति उत्पन्न करता है लेकिन अंतराल-से-अंतराल रूपांतरण तुरंत लागू नहीं होता है क्योंकि यह bazएक सरणी होने के कारण लागू नहीं होता है । यह खंड 4.1 [conv.lval] में शामिल है जो कहता है:

एक गैर-फ़ंक्शन, गैर-सरणी प्रकार टी का एक चमक (3.10) एक प्रचलन 5। 3 में परिवर्तित किया जा सकता है [...]

सरणी-से-पॉइंटर रूपांतरण में क्या लागू किया जाता है ।

[Basic.def.odr] का यह शब्द दोष रिपोर्ट 712 के कारण बदल दिया गया था क्योंकि कुछ मामले इस शब्दांकन द्वारा कवर नहीं किए गए थे लेकिन ये परिवर्तन इस मामले के परिणामों को नहीं बदलते हैं।


तो क्या हम स्पष्ट हैं कि constexprइससे कोई लेना-देना नहीं है? ( bazवैसे भी एक स्थिर अभिव्यक्ति है)
MM

@MattMcNabb अच्छी तरह से कमी की आवश्यकता होती है यदि सदस्य एक integral or enumeration typeअन्यथा नहीं है, हां, क्या मायने रखता है कि यह एक स्थिर अभिव्यक्ति है
शफीक यघमौर

पहले पैराग्राफ में "ord-used" को "odr-used" के रूप में पढ़ा जाना चाहिए, मेरा मानना ​​है, लेकिन मुझे C ++ के साथ कभी भी यकीन नहीं है
Egor Pasko

37

यह वास्तव में C ++ 11 में एक दोष है - जैसा कि अन्य लोगों ने समझाया है, C ++ 11 में एक स्थिर कॉन्स्टेप सदस्य सदस्य वैरिएबल, प्रत्येक अन्य प्रकार के कॉन्टेक्स वैश्विक चर के विपरीत, बाहरी लिंकेज है, इस प्रकार स्पष्ट रूप से कहीं परिभाषित किया जाना चाहिए।

यह भी ध्यान देने योग्य है कि आप अक्सर अनुकूलन के साथ संकलन करते समय स्थिर कॉन्स्ट्रेक्स के सदस्य चर के साथ दूर हो सकते हैं, क्योंकि वे सभी उपयोगों में इनलाइन को समाप्त कर सकते हैं, लेकिन यदि आप अनुकूलन के बिना संकलन करते हैं, तो अक्सर आपका प्रोग्राम लिंक करने में विफल होगा। यह एक बहुत ही सामान्य छिपा हुआ जाल बनाता है - आपका कार्यक्रम अनुकूलन के साथ ठीक संकलन करता है, लेकिन जैसे ही आप अनुकूलन बंद करते हैं (शायद डिबगिंग के लिए), यह लिंक करने में विफल रहता है।

हालांकि अच्छी खबर - यह दोष C ++ 17 में तय किया गया है! दृष्टिकोण थोड़ा जटिल है, हालांकि: C ++ 17 में, स्थिर कॉन्स्टैक्स सदस्य चर स्पष्ट रूप से इनलाइन हैंचर के लिए लागू इनलाइन होने से C ++ 17 में एक नई अवधारणा है, लेकिन इसका प्रभावी रूप से मतलब है कि उन्हें कहीं भी स्पष्ट परिभाषा की आवश्यकता नहीं है।


4
C ++ 17 जानकारी के लिए। आप स्वीकृत उत्तर के लिए इस जानकारी को जोड़ सकते हैं!
एसआर

5

अधिक सुरुचिपूर्ण समाधान में नहीं बदल रहा है char[]:

static constexpr char * baz = "quz";

इस तरह से हमारे पास कोड की 1 पंक्ति में परिभाषा / घोषणा / इनिशियलाइज़र हो सकता है।


9
साथ char[]आप उपयोग कर सकते sizeofसंकलन समय पर स्ट्रिंग की लंबाई प्राप्त करने के लिए, के साथ char *आप नहीं (यह सूचक प्रकार, 1 की चौड़ाई इस मामले में वापस आ जाएगी) कर सकते हैं।
gnzlbg

2
यदि आप ISO C ++ 11 के साथ सख्त होना चाहते हैं तो यह चेतावनी भी उत्पन्न करता है।
शीतल शाह

मेरा जवाब देखें जो इस sizeofमुद्दे को प्रदर्शित नहीं करता है , और इसका उपयोग "हेडर-ओनली" समाधान में किया जा सकता है
जोश ग्रीफर

4

स्थैतिक सदस्यों के बाहरी जुड़ाव के लिए मेरा काम constexprसंदर्भ सदस्य गेटर्स का उपयोग करना है (जो @gedzlbg समस्या में नहीं चलता है, @deddebme से उत्तर के लिए एक टिप्पणी के रूप में उठाया गया है)।
यह मुहावरा मेरे लिए महत्वपूर्ण है क्योंकि मैं अपनी परियोजनाओं में कई .cpp फाइलें रखता हूं, और संख्या को एक तक सीमित करने की कोशिश करता हूं, जिसमें कुछ नहीं बल्कि #includes और एक main()फ़ंक्शन होता है।

// foo.hpp
struct foo {
  static constexpr auto& baz() { return "quz"; }
};

// some.cpp

  auto sz = sizeof(foo::baz()); // sz == 4

  auto& foo_baz = foo::baz();  // note auto& not auto
  auto sz2 =  sizeof(foo_baz);    // 4
  auto name = typeid(foo_baz).name();  // something like 'char const[4]'

-1

मेरे वातावरण पर, gcc vesion 5.4.0 है। "-O2" जोड़ना इस संकलन त्रुटि को ठीक कर सकता है। ऐसा लगता है कि अनुकूलन के लिए पूछने पर यह मामला इस मामले को संभाल सकता है।

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