स्थिर कॉन्स्टीट इंट का अपरिभाषित संदर्भ


79

मैं आज एक दिलचस्प मुद्दे पर भाग गया। इस सरल उदाहरण पर विचार करें:

template <typename T>
void foo(const T & a) { /* code */ }

// This would also fail
// void foo(const int & a) { /* code */ }

class Bar
{
public:
   static const int kConst = 1;
   void func()
   {
      foo(kConst);           // This is the important line
   }
};

int main()
{
   Bar b;
   b.func();
}

संकलन करते समय मुझे एक त्रुटि मिलती है:

Undefined reference to 'Bar::kConst'

अब, मुझे पूरा यकीन है कि ऐसा इसलिए है क्योंकि यह static const intकहीं भी परिभाषित नहीं है, जो जानबूझकर किया गया है क्योंकि मेरी समझ के अनुसार संकलनकर्ता को संकलन-समय पर प्रतिस्थापन करने में सक्षम होना चाहिए और परिभाषा की आवश्यकता नहीं होनी चाहिए। हालाँकि, चूंकि फ़ंक्शन एक const int &पैरामीटर लेता है , ऐसा लगता है कि यह प्रतिस्थापन नहीं बना रहा है, और इसके बजाय एक संदर्भ को प्राथमिकता दे रहा है। मैं निम्नलिखित परिवर्तन करके इस समस्या को हल कर सकता हूं:

foo(static_cast<int>(kConst));

मेरा मानना ​​है कि यह अब संकलक को एक अस्थायी इंट बनाने के लिए मजबूर कर रहा है, और फिर उस संदर्भ को पास करें, जिसे वह सफलतापूर्वक संकलन समय पर कर सकता है।

मैं सोच रहा था कि क्या यह जानबूझकर किया गया था, या मैं इस मामले को संभालने में सक्षम होने के लिए जीसीसी से बहुत अधिक उम्मीद कर रहा हूं? या यह कुछ ऐसा है जो मुझे किसी कारण से नहीं करना चाहिए?


1
व्यवहार में आप बस const int kConst = 1;उसी परिणाम के साथ कर सकते हैं । इसके अलावा, वहाँ शायद ही कभी एक कारण है (मैं कोई नहीं सोच सकते हैं) एक समारोह प्रकार के एक पैरामीटर लेने के लिए const int &- बस intयहाँ का उपयोग करें।
ब्योर्न पोलेक्स

1
@स्पेस वास्तविक फ़ंक्शन एक टेम्पलेट था, मैं इसका उल्लेख करने के लिए अपने प्रश्न को संपादित करूंगा।
JaredC

1
@स्पेस फ़िजी, इसे नहीं बनाना staticएक त्रुटि देता है `आईएसओ C ++ सदस्य के आरंभ के मना करता है 'kConst' ... 'kConst' को स्थिर बनाता है। '
JaredC

मेरा बुरा, सुधार के लिए धन्यवाद।
ब्योर्न पोलेक्स

1
कष्टप्रद बात यह त्रुटि की तरह अहानिकर उपयोग में दिखाई दे सकते हैं, है std::min( some_val, kConst), के बाद से std::min<T>प्रकार के मापदंडों है T const &, और निहितार्थ यह है कि हम के लिए एक संदर्भ में उत्तीर्ण होना है kConst। मैंने पाया कि यह केवल तब हुआ जब अनुकूलन बंद था। एक स्थिर कलाकार का उपयोग कर फिक्स्ड।
ग्रेगोगो

जवाबों:


61

यह जानबूझकर है, 9.4.2 / 4 कहते हैं:

यदि कोई स्थैतिक डेटा सदस्य कास्ट इंटीग्रल या कास्ट एन्यूमरेशन प्रकार का होता है, तो क्लास डेफिनेशन में इसकी घोषणा एक कंटिन्यू-इनिशियलाइज़र को निर्दिष्ट कर सकती है, जो एक इंटीग्रल कंटीन्यूअस एक्सप्रेशन (5.19) होगा। उस स्थिति में, सदस्य इंटीग्रल कंटीन्यूअस एक्सप्रेशन में दिखाई दे सकता है। यदि प्रोग्राम में इसका उपयोग किया जाता है, तो सदस्य को अभी भी नाम स्थान के दायरे में परिभाषित किया जाएगा

जब आप स्थैतिक डेटा सदस्य को संदर्भ द्वारा पास करते हैं, तो आप इसे "3.2" / 2 का उपयोग करते हैं:

एक अभिव्यक्ति का संभावित रूप से मूल्यांकन किया जाता है जब तक कि यह प्रकट नहीं होता है जहां एक अभिन्न स्थिर अभिव्यक्ति की आवश्यकता होती है (5.19 देखें), आकारऑपर ऑपरेटर (5.3.3) का ऑपरेंड है, या टाइपिड ऑपरेटर का ऑपरेंड है और एक्सप्रेशन को एक प्रतिमा नामित नहीं करता है बहुरूपी वर्ग प्रकार (5.2.8)। एक ऑब्जेक्ट या गैर-अतिभारित फ़ंक्शन का उपयोग किया जाता है यदि इसका नाम संभावित मूल्यांकन वाली अभिव्यक्ति में दिखाई देता है।

तो वास्तव में, आप इसे "उपयोग" करते हैं जब आप इसे मूल्य से भी पास करते हैं, या एक में static_cast। यह सिर्फ इतना है कि जीसीसी ने आपको एक मामले में हुक बंद करने दिया है, लेकिन दूसरे को नहीं।

[संपादित करें: gcc, C ++ 0x ड्राफ्ट से नियमों को लागू कर रहा है: "एक चर या गैर-अतिभारित फ़ंक्शन जिसका नाम संभावित-मूल्यांकन की गई अभिव्यक्ति के रूप में प्रकट होता है, जब तक कि यह एक ऑब्जेक्ट नहीं है, जो एक निरंतरता में दिखाई देने वाली आवश्यकताओं को संतुष्ट करता है। अभिव्यक्ति (५.१ ९) और लैवल्यू-टू-रैवल्यू रूपांतरण (४.१) तुरंत लागू होता है। " स्टैटिक कास्ट लैवल्यू-रेवल्यू रूपांतरण तुरंत करता है, इसलिए C ++ 0x में यह "उपयोग" नहीं किया जाता है।]

कास्ट संदर्भ के साथ व्यावहारिक समस्या यह है कि fooअपने तर्क के पते को लेने के अपने अधिकारों के भीतर है, और उदाहरण के लिए एक वैश्विक कॉल में संग्रहीत किसी अन्य कॉल से तर्क के पते के साथ तुलना करें। चूंकि स्थैतिक डेटा सदस्य एक अद्वितीय ऑब्जेक्ट है, इसका मतलब है कि यदि आप foo(kConst)दो अलग-अलग टीयू से कॉल करते हैं , तो पास की गई वस्तु का पता प्रत्येक मामले में समान होना चाहिए। AFAIK GCC व्यवस्था नहीं कर सकता है कि जब तक कि वस्तु एक (और केवल एक) TU में परिभाषित न हो।

ठीक है, इसलिए इस मामले fooमें एक टेम्पलेट है, इसलिए सभी टीयू में परिभाषा दिखाई देती है, इसलिए शायद संकलक सिद्धांत में जोखिम का पता लगा सकता है कि यह पते के साथ कुछ भी करता है। लेकिन सामान्य तौर पर आप निश्चित रूप से गैर-मौजूद वस्तुओं के पते या संदर्भ नहीं लेना चाहिए ;-)


1
संदर्भ का पता लेने के उदाहरण के लिए धन्यवाद। मुझे लगता है कि यह एक वास्तविक व्यावहारिक कारण है कि कंपाइलर वह नहीं करता है जो मैं उम्मीद करता हूं।
JaredC

पूर्ण अनुरूपता के लिए, मुझे लगता है कि आपको कुछ परिभाषित करना होगा template <int N> int intvalue() { return N; }। तब intvalue<kConst>, kConstकेवल एक संदर्भ में एक अभिन्न निरंतर अभिव्यक्ति की आवश्यकता होती है, और इसलिए इसका उपयोग नहीं किया जाता है। लेकिन फ़ंक्शन उसी मान के साथ एक अस्थायी लौटाता है kConst, और जो एक कॉन्स्टेंट रेफरेंस को बाध्य कर सकता है। मुझे यकीन नहीं है, हालांकि, इसे लागू kConstनहीं करने के लिए एक सरल तरीका हो सकता है ।
स्टीव जेसप

1
मैं एक ही समस्या का अनुभव करता हूँ r = s ? kConst1 : kConst2gern 4.7 के साथ एक ternary ऑपरेटर (जैसे कुछ ) में इस तरह के स्थिर कॉन्स्टेबल चर का उपयोग करके । मैंने एक वास्तविक का उपयोग करके इसे हल किया if। वैसे भी उत्तर के लिए धन्यवाद!
क्लोड्रिक

2
... और एसटीडी :: मिनट / एसटी :: अधिकतम, जिसने मुझे यहां पहुंचाया!
ऋषि

"AFAIK GCC यह व्यवस्था नहीं कर सकती कि जब तक कि वस्तु को एक (और केवल एक) TU में परिभाषित न किया जाए"। यह बहुत बुरा है, क्योंकि यह पहले से ही संभव है कि स्थिरांक के साथ - इनकोटाटा में 'कमजोर दोष' के रूप में कई बार संकलित करें, और फिर लिंकर को केवल एक चुनना होगा - जो यह सुनिश्चित करता है कि सभी वास्तविक रेफ्स का समान पता होगा। यह वास्तव में टाइपिड के लिए क्या किया जाता है; यह अजीब तरीकों से विफल हो सकता है, हालांकि, जब साझा देयता का उपयोग किया जाता है।
21

27

यदि आप कक्षा की घोषणा के अंदर इनिशियलाइज़र के साथ स्थिर कास्ट वैरिएबल लिख रहे हैं तो यह वैसा ही है जैसे आपने लिखा है

class Bar
{
      enum { kConst = 1 };
}

और जीसीसी इसे उसी तरह से व्यवहार करेगा, जिसका अर्थ है कि इसका कोई पता नहीं है।

सही कोड होना चाहिए

class Bar
{
      static const int kConst;
}
const int Bar::kConst = 1;

इस उदाहरण के लिए धन्यवाद।
शुहलो

12

यह वास्तव में मान्य मामला है। विशेष रूप से क्योंकि fd एसटीएल से एक फ़ंक्शन हो सकता है जैसे std :: count जो एक कॉन्स्टेबल T & को इसके तीसरे तर्क के रूप में लेता है ।

मैंने यह समझने में बहुत समय बिताया कि लिंकर को इस तरह के मूल कोड की समस्या क्यों है।

त्रुटि संदेश

'बार :: kConst' का अपरिभाषित संदर्भ

हमें बताता है कि लिंकर को एक प्रतीक नहीं मिल सकता है।

$nm -C main.o
0000000000000000 T main
0000000000000000 W void foo<int>(int const&)
0000000000000000 W Bar::func()
0000000000000000 U Bar::kConst

हम 'U' से देख सकते हैं कि Bar :: kConst अपरिभाषित है। इसलिए, जब लिंकर अपना काम करने की कोशिश करता है, तो उसे प्रतीक ढूंढना पड़ता है। लेकिन आप केवल kConst की घोषणा करते हैं और इसे परिभाषित नहीं करते हैं।

C ++ में समाधान भी इसे इस प्रकार परिभाषित करना है:

template <typename T>
void foo(const T & a) { /* code */ }

class Bar
{
public:
   static const int kConst = 1;
   void func()
   {
      foo(kConst);           // This is the important line
   }
};

const int Bar::kConst;       // Definition <--FIX

int main()
{
   Bar b;
   b.func();
}

फिर, आप देख सकते हैं कि संकलक उत्पन्न की गई फ़ाइल में परिभाषा डाल देगा:

$nm -C main.o
0000000000000000 T main
0000000000000000 W void foo<int>(int const&)
0000000000000000 W Bar::func()
0000000000000000 R Bar::kConst

अब, आप 'R' को यह कहते हुए देख सकते हैं कि यह डेटा सेक्शन में परिभाषित है।


क्या यह ठीक है कि एक "आर" और एक पते के साथ पहले "एनएम" आउटपुट में दो बार एक निरंतरता दिखाई देती है, और फिर "यू" के साथ?
मात्रा_देव

क्या आपके पास कोई उदाहरण है? प्रदान किए गए उदाहरण पर, >nm -C main.o | grep kConstमुझे केवल एक पंक्ति मिलती है 0000000000400644 R Bar::kConst
स्टैक

स्टैटिक लाइब्रेरी संकलित करते समय मैं इसे देखता हूं।
मात्रा_देव

1
इस मामले में, निश्चित रूप से! एक स्थिर पुस्तकालय केवल ऑब्जेक्ट फ़ाइलों का एक समुच्चय है। लिंकेज केवल स्टेटिक लिब के क्लाइंट द्वारा किया जाता है। इसलिए यदि आप आर्काइव फ़ाइल में रखते हैं, तो ऑब्स्ट्रक्ट फाइल जिसमें कास्ट की परिभाषा है और एक और ऑब्जेक्ट फाइल है, जिसे Bar :: func () कहते हैं, आपको सिंबल के साथ एक बार और बिना एक बार सिंबल दिखाई देगा: nm -C lib.aआपको Constants.o: 0000000000000000 R Bar::kConstऔर main_file.o: U Bar::kConst ...
स्टैक

2

g ++ संस्करण 4.3.4 इस कोड को स्वीकार करता है ( इस लिंक को देखें )। लेकिन जी ++ संस्करण 4.4.0 इसे अस्वीकार करता है।


2

मुझे लगता है कि C ++ का यह मतलब है कि किसी भी समय इसका Bar::kConstउल्लेख किया जाता है, इसके बजाय इसके शाब्दिक मूल्य का उपयोग किया जाता है।

इसका मतलब है कि व्यवहार में संदर्भ बिंदु बनाने के लिए कोई चर नहीं है।

आपको यह करना पड़ सकता है:

void func()
{
  int k = kConst;
  foo(k);
}

यह मूल रूप से है जो मैं इसे बदलकर प्राप्त कर रहा था foo(static_cast<int>(kConst));, है ना?
JaredC

2

आप इसे एक सदस्यीय सदस्य कार्य द्वारा भी बदल सकते हैं:

class Bar
{
  static constexpr int kConst() { return 1; };
};

ध्यान दें कि यह "निरंतर" के बाद घोषणा और ब्रेसिज़ दोनों में 4 लाइनों की आवश्यकता होती है, इसलिए आप foo = std :: num_limits <int> :: max () * बार :: this_is_a_constant_ts_looks_like_a_method () लिखना और अपने कोडिंग मानक कॉप की अपेक्षा करना और आशावादी आपके लिए इसे ठीक करता है।
कोड एबोमिनेटर

1

सिंपल ट्रिक: फंक्शन डाउन +होने से पहले उपयोग करें kConst। इससे कंटीन्यू को रेफरेंस लेने से रोका जा सकेगा, और इस तरह से कोड कंटिन्यू ऑब्जेक्ट के लिए लिंकर रिक्वेस्ट जेनरेट नहीं करेगा, बल्कि यह कंपाइलर-टाइम कंटीन्यू वैल्यू के बजाय आगे बढ़ेगा।


यह अफ़सोस की बात है कि संकलक एक चेतावनी जारी नहीं करता है जब एक पते को एक static constमूल्य से लिया जाता है जिसे घोषणा में आरम्भ किया जाता है। यह हमेशा लिंकर त्रुटि का कारण बनेगा, और जब एक ही फ़ाइल में समान स्थिरांक को भी अलग से घोषित किया जाता है, तो यह एक त्रुटि होगी। संकलक भी स्थिति से पूरी तरह अवगत है।
इथोरिस

संदर्भों को अस्वीकार करने का सबसे अच्छा तरीका क्या है? मैं वर्तमान में कर रहा हूँ static_cast<decltype(kConst)>(kConst)
वेलकन

@Velkan मैं जानना चाहता हूँ कि वह भी कैसे करना है। आपका tatic_cast <घोषणापत्र (kConst)> (kConst) ट्रिक इस मामले में काम नहीं करता है कि kConst एक चार [64] है; इसे "त्रुटि: static_cast '' char * 'से' घोषणापत्र (start_time) '(उर्फ' char [64] ') की अनुमति नहीं है"।
डॉन हैच

@ डॉन्चैच, मैं सॉफ्टवेयर पुरातत्व में नहीं हूं, लेकिन जहां तक ​​मुझे याद है कि नकल के माध्यम से एक कच्चे सरणी को फ़ंक्शन में पारित करना बहुत कठिन है। तो foo()मूल रूप से मूल प्रश्न से पते की आवश्यकता होगी, और इसे पूरे सरणी की अस्थायी प्रतिलिपि के रूप में व्यवहार करने के लिए कोई तंत्र नहीं है।
वेलकन

0

मैंने क्लोडेरिक द्वारा वर्णित एक ही समस्या का अनुभव किया (एक सहायक ऑपरेटर में स्थिर गति:) r = s ? kConst1 : kConst2, लेकिन कंपाइलर ऑप्टिमाइज़ेशन (-O0 के बजाय -O) को बंद करने के बाद ही इसकी शिकायत की। Gcc पर कोई नहीं-कोई भी 4.8.5 पर हुआ।

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