संदर्भ C++ में "कॉन्स्टेबल" क्यों नहीं हैं?




reference const (4)

हम जानते हैं कि "कॉन्स्टेबल वैरिएबल" इंगित करता है कि एक बार असाइन करने के बाद, आप वैरिएबल को इस तरह से नहीं बदल सकते:

int const i = 1;
i = 2;

उपरोक्त कार्यक्रम संकलन में विफल रहेगा; एक त्रुटि के साथ संकेत देता है:

assignment of read-only variable 'i'

कोई समस्या नहीं, मैं इसे समझ सकता हूं, लेकिन निम्नलिखित उदाहरण मेरी समझ से परे है:

#include<iostream>
using namespace std;
int main()
{
    boolalpha(cout);
    int const i = 1;
    cout << is_const<decltype(i)>::value << endl;
    int const &ri = i;
    cout << is_const<decltype(ri)>::value << endl;
    return 0;
}

यह आउटपुट करता है

true
false

अजीब। हम जानते हैं कि एक बार एक संदर्भ एक नाम / चर के लिए बाध्य है, हम इस बंधन को नहीं बदल सकते हैं, हम इसकी बाध्य वस्तु को बदलते हैं। तो मुझे लगता है कि ri का प्रकार i जैसा ही होना चाहिए: जब i एक int const i , तो ri const क्यों नहीं है?


"री" "कांस्ट" क्यों नहीं है?

std::is_const जाँचता है कि प्रकार std::is_const योग्य है या नहीं।

यदि T एक कांस्टेबल-योग्य प्रकार है (जो कि, const, या const वाष्पशील है), तो सदस्य को निरंतर मूल्य समान सत्य प्रदान करता है। किसी अन्य प्रकार के लिए, मूल्य गलत है।

लेकिन संदर्भ कांस्टेबल योग्य नहीं हो सकता। सन्दर्भ [dcl.ref] / १

Cv- योग्य संदर्भ तब बनाए जाते हैं, सिवाय इसके कि जब cv-qualifiers टाइप-एफ-नेम ([dcl.typedef], [temp.param]) या डिक्वेस्ट-स्पेसियर ([dcl.type.simple]) के उपयोग के माध्यम से पेश किए जाते हैं जिस स्थिति में सीवी-क्वालीफायर को नजरअंदाज किया जाता है।

तो is_const<decltype(ri)>::value false वापस is_const<decltype(ri)>::value ri (संदर्भ) एक कास्ट-योग्य प्रकार नहीं है। जैसा कि आपने कहा, हम आरंभीकरण के बाद एक संदर्भ का खंडन नहीं कर सकते हैं, जिसका अर्थ है कि संदर्भ हमेशा "कॉन्स्ट" होता है, दूसरी ओर, कास्ट-क्वालीफाइड रेफरेंस या कॉन्स्टल-अनक्लाइफाइड रेफरेंस वास्तव में समझ में नहीं आता है।


const x & x ”का अर्थ है x उपनाम X वस्तु। लेकिन आप उस X वस्तु को x के माध्यम से नहीं बदल सकते।

और std::is_const देखें।


मैक्रों को कब्ज क्यों नहीं है? कार्य? शाब्दिक? प्रकारों के नाम?

const बातें केवल अपरिवर्तनीय चीजों का एक सबसेट हैं।

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

यदि C ++ में डिफ़ॉल्ट रूप से अपरिवर्तनीय वस्तुएं होती हैं, तो किसी भी चीज पर mutable कीवर्ड की आवश्यकता होती है, जिसे आप mutable नहीं बनाना चाहते हैं, तो यह आसान होगा: बस प्रोग्रामर को संदर्भ प्रकारों में mutable जोड़ने की अनुमति न दें।

जैसा कि है, वे योग्यता के बिना अपरिवर्तनीय हैं।

और, चूंकि वे नक्षत्रीय नहीं हैं, इसलिए शायद यह सही साबित करने के लिए एक संदर्भ प्रकार पर is_const लिए अधिक भ्रामक होगा।

मुझे यह एक उचित समझौता लगता है, खासकर जब से अपरिवर्तनीयता वैसे भी केवल इस तथ्य से लागू होती है कि कोई वाक्यविन्यास किसी संदर्भ को बदलने के लिए मौजूद नहीं है।


यह C ++ में क्विकर / फीचर है। यद्यपि हम संदर्भों को प्रकारों के बारे में नहीं सोचते हैं, वे वास्तव में प्रकार प्रणाली में "बैठते हैं"। हालांकि यह अजीब लगता है (यह देखते हुए कि जब संदर्भ का उपयोग किया जाता है, तो संदर्भ शब्दार्थ स्वचालित रूप से होता है और संदर्भ "रास्ते से बाहर हो जाता है"), कुछ बचाव योग्य कारण हैं कि संदर्भों को एक अलग विशेषता के रूप में सिस्टम में टाइप करने के बजाय बाहर क्यों रखा गया है प्रकार।

सबसे पहले, हम इस बात पर विचार करें कि घोषित प्रणाली की प्रत्येक विशेषता प्रकार प्रणाली में नहीं होनी चाहिए। सी भाषा से, हमारे पास "भंडारण वर्ग", और "लिंकेज" है। एक नाम को extern const int ri रूप में पेश किया जा सकता है, जहां extern स्टेटिक स्टोरेज क्लास और लिंकेज की उपस्थिति को इंगित करता है। प्रकार सिर्फ const int

C ++ स्पष्ट रूप से इस धारणा को स्वीकार करता है कि अभिव्यक्तियों में ऐसी विशेषताएँ हैं जो टाइप सिस्टम के बाहर हैं। अब भाषा में "मूल्य वर्ग" की अवधारणा है जो गैर-प्रकार की विशेषताओं की बढ़ती संख्या को व्यवस्थित करने का एक प्रयास है जो एक अभिव्यक्ति का प्रदर्शन कर सकती है।

फिर भी संदर्भ प्रकार हैं। क्यूं कर?

यह C ++ ट्यूटोरियल्स में बताया गया है कि const int &ri जैसे डिक्लेरेशन ने const int &ri को टाइप const int , लेकिन रेफरेंस शब्दार्थ के रूप में पेश किया। वह संदर्भ शब्दार्थ एक प्रकार का नहीं था; यह केवल नाम और भंडारण स्थान के बीच एक असामान्य संबंध का संकेत देने वाला एक प्रकार का गुण था। इसके अलावा, यह तथ्य कि संदर्भ प्रकार नहीं हैं, का उपयोग इस बात को तर्कसंगत बनाने के लिए किया गया था कि आप संदर्भ के आधार पर प्रकारों का निर्माण क्यों नहीं कर सकते हैं, भले ही प्रकार निर्माण वाक्यविन्यास इसकी अनुमति देता हो। उदाहरण के लिए, सरणियाँ या संकेत संभव नहीं होने के संदर्भ में: const int &ari[5] और const int &*pri

लेकिन वास्तव में संदर्भ प्रकार होते हैं और इसलिए decltype(ri) कुछ संदर्भ प्रकार के नोड को पुनः प्राप्त करता है जो कि अयोग्य है। आपको इस प्रकार को पेड़ में उस प्रकार से उतरना होगा, जिसे remove_reference लिए पेड़ को remove_reference साथ अंतर्निहित प्रकार में remove_reference

जब आप ri उपयोग करते हैं, तो संदर्भ को पारदर्शी रूप से हल किया जाता है, ताकि ri "जैसा दिखता है और i लगता है" और इसके लिए इसे "उपनाम" कहा जा सकता है। प्रकार प्रणाली में, हालांकि, ri वास्तव में एक प्रकार है जो " const int संदर्भ है "।

संदर्भ प्रकार क्यों हैं?

विचार करें कि यदि संदर्भ प्रकार नहीं थे, तो इन कार्यों को उसी प्रकार माना जाएगा:

void foo(int);
void foo(int &);

यह केवल उन कारणों के लिए नहीं हो सकता है जो बहुत अधिक स्पष्ट हैं। यदि उनके पास एक ही प्रकार होता है, तो इसका मतलब है कि या तो घोषणा या तो परिभाषा के लिए उपयुक्त होगी, और इसलिए प्रत्येक (int) फ़ंक्शन को संदर्भ लेने पर संदेह करना होगा।

इसी तरह, यदि संदर्भ प्रकार नहीं थे, तो ये दो श्रेणी की घोषणाएं समान होंगी:

class foo {
  int m;
};

class foo {
  int &m;
};

एक घोषणा इकाई के लिए एक घोषणा का उपयोग करना सही होगा, और दूसरी घोषणा का उपयोग करने के लिए उसी कार्यक्रम में एक और अनुवाद इकाई।

तथ्य यह है कि एक संदर्भ के कार्यान्वयन में अंतर होता है और इसे उस प्रकार से अलग करना असंभव है, क्योंकि सी ++ में टाइप को एक इकाई के कार्यान्वयन के साथ करना है: बोलने के लिए बिट्स में इसका "लेआउट"। यदि दो फ़ंक्शन एक ही प्रकार के हैं, तो उन्हें एक ही बाइनरी कॉलिंग सम्मेलनों के साथ लागू किया जा सकता है: एबीआई एक ही है। यदि दो संरचनाओं या वर्गों में एक ही प्रकार है, तो उनका लेआउट समान है और साथ ही सभी सदस्यों तक पहुंच के शब्दार्थ भी हैं। संदर्भों की उपस्थिति प्रकार के इन पहलुओं को बदल देती है, और इसलिए यह उन्हें प्रकार प्रणाली में शामिल करने के लिए एक सीधा डिजाइन निर्णय है। (हालांकि, यहां एक प्रतिवाद पर ध्यान दें: एक संरचना / वर्ग का सदस्य static हो सकता है, जो प्रतिनिधित्व को भी बदलता है; फिर भी वह टाइप नहीं है!)

इस प्रकार, संदर्भ प्रकार में हैं प्रणाली "द्वितीय श्रेणी के नागरिक" (आईएसओ सी में कार्यों और सरणियों के विपरीत नहीं)। ऐसी कुछ चीजें हैं जो हम संदर्भों के साथ "नहीं" कर सकते हैं, जैसे कि संदर्भकर्ताओं को संदर्भों की घोषणा करना, या उनमें से सरणियाँ। लेकिन इसका मतलब यह नहीं है कि वे टाइप नहीं हैं। वे सिर्फ एक तरह से टाइप नहीं हैं कि यह समझ में आता है।

ये सभी द्वितीय श्रेणी-प्रतिबंध आवश्यक नहीं हैं। यह देखते हुए कि संदर्भों की संरचनाएं हैं, संदर्भों की सरणियाँ हो सकती हैं! उदाहरण के लिए

// fantasy syntax
int x = 0, y = 0;
int &ar[2] = { x, y };

// ar[0] is now an alias for x: could be useful!

यह सिर्फ C ++ में लागू नहीं है, बस इतना ही। संदर्भ के लिए संकेत बिल्कुल भी समझ में नहीं आता है, क्योंकि एक संदर्भ से उठाया गया एक संकेतक सिर्फ संदर्भित वस्तु पर जाता है। संदर्भों की कोई सरणियाँ नहीं होने का संभावित कारण यह है कि C ++ लोग C से विरासत में मिली एक प्रकार की निम्न-स्तरीय विशेषता मानते हैं जो कई प्रकार से टूटी हुई है जो अपूरणीय है, और वे सरणियों को स्पर्श नहीं करना चाहते हैं। कुछ भी नया करने का आधार। संदर्भों के सरणियों का अस्तित्व, हालांकि, इस बात का स्पष्ट उदाहरण देगा कि संदर्भों को किस प्रकार का होना चाहिए।

गैर-कब्ज-योग्य प्रकार: आईएसओ C90 में भी पाया जाता है!

कुछ जवाब इस तथ्य पर इशारा कर रहे हैं कि संदर्भ एक const नहीं लेते हैं। यह बल्कि एक लाल हेरिंग है, क्योंकि घोषणा पत्र const int &ri = i भी एक कांस्टेबल-अयोग्य संदर्भ बनाने का प्रयास नहीं कर रहा है: यह एक कॉन्स्टेबल-योग्य प्रकार (जो स्वयं कांस्ट नहीं है) का संदर्भ है। जैसे const in *ri किसी न किसी const को पॉइंटर घोषित करता है, लेकिन वह पॉइंटर खुद const नहीं है।

इसने कहा, यह सच है कि संदर्भ const को खुद नहीं ले जा सकते हैं।

फिर भी, यह इतना विचित्र नहीं है। यहां तक ​​कि आईएसओ सी 90 भाषा में, सभी प्रकारों को const नहीं किया जा सकता। अर्थात्, सरणियाँ नहीं हो सकतीं।

सबसे पहले, वाक्य रचना एक कॉन्स्टेंट ऐरे की घोषणा के लिए मौजूद नहीं है: int a const [42] गलत है।

हालांकि, उपरोक्त घोषणा क्या करने की कोशिश कर रही है, एक मध्यवर्ती typedef माध्यम से व्यक्त किया जा सकता है:

typedef int array_t[42];
const array_t a;

लेकिन यह ऐसा नहीं करता जैसा दिखता है। इस घोषणा में, यह कोई ऐसा नहीं a जो योग्य हो, लेकिन तत्व! कहने का const int यह है कि a[0] एक const int , लेकिन a "इंट ऑफ अरेंजमेंट" है। नतीजतन, इसके लिए निदान की आवश्यकता नहीं है:

int *p = a; /* surprise! */

यह करता है:

a[0] = 1;

फिर, यह इस विचार को रेखांकित करता है कि संदर्भ कुछ प्रकार के "द्वितीय श्रेणी" में हैं, जैसे कि सरणियाँ।

ध्यान दें कि अनुरूप कैसे अधिक गहराई से पकड़ता है, क्योंकि सरणियों में भी "अदृश्य रूपांतरण व्यवहार" होता है, जैसे संदर्भ। प्रोग्रामर के बिना किसी भी स्पष्ट ऑपरेटर का उपयोग करने के लिए, पहचानकर्ता स्वचालित रूप से एक int * पॉइंटर में बदल जाता है, जैसे कि अभिव्यक्ति &a[0] का उपयोग किया गया था। यह एक संदर्भ कैसे है, जब हम इसे एक प्राथमिक अभिव्यक्ति के रूप में उपयोग करते हैं, तो यह जादुई रूप से उस वस्तु को दर्शाता है, जिसके लिए यह बाध्य है। यह "सरणी से सूचक क्षय" की तरह सिर्फ एक और "क्षय" है।

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

जब decltype(ri) संदर्भ की सामान्य रूपांतरण को उसकी संदर्भ वस्तु में दबा देता है, तो यह sizeof a -से-सूचक सूचक को दबाने वाले sizeof a से इतना अलग नहीं है, और इसके आकार की गणना करने के लिए सरणी प्रकार पर ही काम करता है।






decltype