फ़ंक्शन मापदंडों के लिए 'कॉन्स्ट' का उपयोग


396

आप कितनी दूर तक जाते हैं const? क्या आप केवल constआवश्यक होने पर कार्य करते हैं या आप पूरे हॉग जाते हैं और हर जगह इसका उपयोग करते हैं? उदाहरण के लिए, एक साधारण उत्परिवर्ती की कल्पना करें जो एकल बूलियन पैरामीटर लेता है:

void SetValue(const bool b) { my_val_ = b; }

क्या यह constवास्तव में उपयोगी है? व्यक्तिगत रूप से मैं इसे बड़े पैमाने पर उपयोग करने का विकल्प चुनता हूं, जिसमें पैरामीटर भी शामिल हैं, लेकिन इस मामले में मुझे आश्चर्य है कि क्या यह सार्थक है?

मुझे यह जानकर भी आश्चर्य हुआ कि आप constएक फ़ंक्शन घोषणा में मापदंडों से चूक सकते हैं लेकिन इसे फ़ंक्शन परिभाषा में शामिल कर सकते हैं, जैसे:

.h फ़ाइल

void func(int n, long l);

.cpp फ़ाइल

void func(const int n, const long l)

क्या इसका कोई कारण है? यह मुझे थोड़ा असामान्य लगता है।


मैं असहमत हूं। .H फ़ाइल के रूप में अच्छी तरह से परिभाषा परिभाषा होनी चाहिए। यदि नहीं, तो यदि फंक्शन में कॉन्स्ट पैरामीटर को पास किया जाता है, तो कंपाइलर एक त्रुटि उत्पन्न करेगा, क्योंकि .h फ़ाइल में प्रोटोटाइप के पास कॉस्ट परिभाषाएँ नहीं होती हैं।
सेल्विन

10
मैं सहमत हूँ। :-) (प्रश्न के साथ, अंतिम टिप्पणी नहीं!) यदि फ़ंक्शन के शरीर में कोई मान नहीं बदलना चाहिए, तो यह मूर्खतापूर्ण तरीके से रोकने में मदद कर सकता है == या = बग्स, आपको दोनों में कभी भी कॉन्स्ट नहीं रखना चाहिए, (यदि यह मूल्य द्वारा पारित किया गया है, आपको अन्यथा होना चाहिए) हालांकि इसके बारे में तर्कों में आना काफी गंभीर नहीं है!
क्रिस हुआंग-लीवर

19
@selwyn: भले ही आप फ़ंक्शन के लिए एक कॉन्स्टेबल इंट पास करते हैं, हालांकि, यह कॉपी किया जा रहा है (क्योंकि यह एक संदर्भ नहीं है), और इसलिए कॉन्स्ट-नेस कोई फर्क नहीं पड़ता।
जालपा

1
इस सवाल में एक ही बहस हो रही है: stackoverflow.com/questions/1554750/…
आंशिक

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

जवाबों:


187

कारण यह है कि पैरामीटर के लिए कास्ट केवल फ़ंक्शन के भीतर स्थानीय रूप से लागू होता है, क्योंकि यह डेटा की एक प्रति पर काम कर रहा है। इसका मतलब है कि फ़ंक्शन हस्ताक्षर वास्तव में वैसे ही हैं। यह शायद एक बहुत ऐसा करने के लिए बुरा शैली है।

मैं व्यक्तिगत रूप से संदर्भ और सूचक मापदंडों को छोड़कर कास्ट का उपयोग नहीं करता हूं। कॉपी की गई वस्तुओं के लिए यह वास्तव में मायने नहीं रखता है, हालांकि यह सुरक्षित हो सकता है क्योंकि यह फ़ंक्शन के भीतर इरादों को इंगित करता है। यह वास्तव में एक निर्णय कॉल है। मैं कॉन्स्टेंटाइटर का उपयोग तब करता हूं, जब किसी चीज पर लूपिंग करता हूं और मैं इसे संशोधित करने का इरादा नहीं रखता हूं, इसलिए मैं प्रत्येक को अपना अनुमान लगाता हूं, जब तक कि संदर्भ प्रकारों के लिए कॉन्स्टेंस शुद्धता कठोरता से बनाए रखी जाती है।


57
मैं 'बुरी शैली' वाले भाग से सहमत नहीं हो सकता। गिराने constसमारोह प्रोटोटाइप से लाभ यह है कि आप हेडर फाइल को बदलने के लिए अगर आप छोड़ने का निर्णय लेते आवश्यकता नहीं है constबाद में कार्यान्वयन भाग से।
मिशाल गौरी

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

3
मैं इस जवाब से असहमत हूं। constजब भी संभव हो मैं दूसरे तरीके से झुकता हूं और मापदंडों को चिह्नित करता हूं; यह अधिक अभिव्यंजक है। जब मैं किसी और के कोड को पढ़ता हूं, तो मैं इस तरह के छोटे संकेतकों का उपयोग करता हूं ताकि यह पता लगाया जा सके कि वे जादू कोड, टिप्पणी, और उचित सूचक उपयोग आदि जैसी चीजों के साथ अपने कोड को लिखने में कितना ध्यान रखते हैं
अल्टिमेटर

4
int getDouble(int a){ ++a; return 2*a; }इसे इस्तेमाल करे। बेशक, ++aवहाँ करने के लिए कुछ भी नहीं है , लेकिन यह एक लंबे समय में एक से अधिक प्रोग्रामर द्वारा लिखे गए लंबे फ़ंक्शन में पाया जा सकता है। मैं दृढ़ता से यह लिखने का सुझाव int getDouble( const int a ){ //... }दूंगा कि खोजने पर एक संकलन त्रुटि उत्पन्न होगी ++a;
डोम_बेऊ

3
यह सभी की बात है कि किसको कौन सी जानकारी चाहिए। आप मान द्वारा पैरामीटर प्रदान करते हैं, इसलिए कॉलर को आपके साथ (आंतरिक रूप से) क्या करना है, इस पर कुछ भी जानने की आवश्यकता नहीं है। इसलिए class Foo { int multiply(int a, int b) const; }अपने हैडर में लिखें । आपके कार्यान्वयन में आप ध्यान रखते हैं कि आप बदलाव नहीं करने का वादा कर सकते हैं aऔर bइसलिए int Foo::multiply(const int a, const int b) const { }यहां समझ में आता है। (सिडेनोट: दोनों कॉलर और कार्यान्वयन देखभाल के बारे में इस तथ्य के बारे में कि फ़ंक्शन अपनी Fooवस्तु को परिवर्तित नहीं करता है , इस प्रकार इसकी घोषणा के अंत में
कास्ट

415

"जब आप कॉल करने वाले की वस्तु को संशोधित नहीं करेंगे तब तर्क को मूल्य से पारित कर दिया जाता है।"

गलत।

यह आपके कोड और आपकी मान्यताओं के स्व-दस्तावेजीकरण के बारे में है।

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

इसके अलावा, जैसा कि किसी ने पहले उल्लेख किया है, यह कंपाइलर को चीजों को थोड़ा अनुकूलित करने में मदद कर सकता है (हालांकि यह एक लंबा शॉट है)।


41
पूर्णतया सहमत। यह लोगों के साथ संवाद करने और एक चर के साथ जो किया जाना चाहिए, उसे प्रतिबंधित करने के बारे में है।
लेन होलगेट

19
मैंने इसे एक वोट दिया है। मुझे लगता है कि जब आप इसे मूल्य तर्क द्वारा सरल पास पर लागू करते हैं, तो आप जो संकेत देते हैं उसे पतला करने की कोशिश करते हैं।
टोनीलो

26
मैंने इसे एक वोट दिया है। पैरामीटर 'कॉन्स्टल' की घोषणा करने से पैरामीटर में अर्थ संबंधी जानकारी जुड़ जाती है। वे हाइलाइट करते हैं कि कोड के मूल लेखक ने क्या इरादा किया था और यह समय के अनुसार कोड के रखरखाव में सहायता करेगा।
रिचर्ड कॉर्डन

13
@tonylo: आप गलत समझते हैं। यह एक स्थानीय चर को कोड के एक ब्लॉक के अंदर कास्ट के रूप में चिह्नित करने के बारे में है (जो कि एक फ़ंक्शन होता है)। मैं किसी भी स्थानीय चर के लिए ऐसा ही करूंगा। यह एक एपीआई है जो कि कास्ट-सही है, जो वास्तव में महत्वपूर्ण है।
rlerallut

56
और यह फंक्शन के अंदर बग्स को पकड़ सकता है - यदि आप जानते हैं कि एक पैरामीटर को नहीं बदला जाना चाहिए, तो इसे कास्ट घोषित करने का मतलब है कि कंपाइलर आपको बताएगा कि क्या आप गलती से इसे संशोधित कर रहे हैं।
एड्रियन

156

कभी-कभी (बहुत बार!) मुझे किसी और के C ++ कोड को अनट्रेंड करना होगा। और हम सभी जानते हैं कि किसी और का सी ++ कोड परिभाषा से लगभग पूरी तरह से गड़बड़ है :) इसलिए स्थानीय डेटा प्रवाह को समझने के लिए पहली बात यह है कि हर चर परिभाषा में कास्ट डाला जाता है जब तक कि कंपाइलर भौंकना शुरू नहीं करता है। इसका मतलब यह है कि कांस्ट क्वालिफाइंग वैल्यू आर्ग्युमेंट्स भी हैं, क्योंकि वे सिर्फ फैंसी लोकल वैरिएबल हैं जिन्हें कॉलर द्वारा इनिशियलाइज़ किया गया है।

आह, मैं चाहता हूं कि चर डिफ़ॉल्ट रूप से कांस्टेबल थे और गैर-कांस्टेबल चर के लिए म्यूटेबल की आवश्यकता थी :)


4
"मैं चाहता हूं कि चर डिफ़ॉल्ट रूप से कास्ट थे" - एक ऑक्सीमोरोन ?? -) गंभीरता से, "कॉन्स्टीट्यूशन" कैसे सब कुछ आपकी मदद करता है जो आपको अनलग कोड बताता है? यदि मूल लेखक ने कथित रूप से निरंतर तर्क को बदल दिया है, तो आप कैसे जानते हैं कि संस्करण एक स्थिर माना जाता था? इसके अलावा, (गैर-तर्क) चर के विशाल बहुमत ... चर रहे हैं। तो कंपाइलर को प्रक्रिया शुरू करने के तुरंत बाद तोड़ देना चाहिए, नहीं?
ysap

8
@ysap, 1. जितना संभव हो सके कब्ज को चिह्नित करने से मैं देख सकता हूं कि कौन से हिस्से चल रहे हैं और कौन से नहीं। मेरे अनुभव में, कई स्थानीय लोग वास्तव में कास्ट हैं, न कि दूसरे तरीके से। 2. "कांस्टेबल वैरिएबल" / "अपरिवर्तनीय चर" ऑक्सिमोरोन के रूप में लग सकता है, लेकिन कार्यात्मक भाषाओं में मानक अभ्यास है, साथ ही कुछ गैर-कार्यात्मक भी हैं; उदाहरण के लिए जंग देखें: doc.rust-lang.org/book/variable-bindings.html
कॉन्स्टैंटिन

1
सी ++ में कुछ परिस्थितियों में भी अब मानक; उदाहरण के लिए, लंबोदर [x](){return ++x;}एक त्रुटि है; यहाँ
अनातोलीग

10
विविधताएं जंगconst में डिफ़ॉल्ट रूप से " " हैं :
फीनिक्स

चर की आवश्यकता नहीं है कि उन्हें अलग-अलग करने की अनुमति देने के लिए असाइन करने योग्य नहीं होना चाहिए; वे जिस मूल्य के साथ आरंभीकृत होते हैं, वह रनटाइम पर भी भिन्न हो सकता है।
स्फिंक्स

80

निम्नलिखित दो लाइनें कार्यात्मक रूप से समतुल्य हैं:

int foo (int a);
int foo (const int a);

जाहिर है कि अगर आप इसे दूसरे तरीके से परिभाषित करते हैं तो आप aशरीर में बदलाव नहीं कर पाएंगे foo, लेकिन बाहर से कोई अंतर नहीं है।

जहां constवास्तव में काम आता है वह संदर्भ या सूचक मापदंडों के साथ है:

int foo (const BigStruct &a);
int foo (const BigStruct *a);

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

* - जब तक यह कास्ट-नेस को दूर नहीं कर देता, लेकिन यह एक और पोस्ट है।


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

> एक कॉन्स्टेंट रेफरेंस पास करने से भी कंपाइलर को कुछ परफॉर्मेंस डिसीजन लेने की सुविधा मिलती है। शास्त्रीय गिरावट - संकलक को अपने लिए कॉन्स्टेबल-नेस का निर्धारण करना होता है, कास्ट कीवर्ड इस बात की मदद नहीं करता है कि पॉइंटर एलियासिंग और कॉन्स्टैस्ट के लिए धन्यवाद
jheriko

73

अतिरिक्त अतिरंजित कास्ट एपीआई स्टैंड-पॉइंट से खराब हैं:

कॉलर या एपीआई उपयोगकर्ता (यह केवल कार्यान्वयन को बाधित करता है) के लिए कोई सार्थक वादा नहीं करते हुए आपके एपीआई के मूल्य सुराग से पारित आंतरिक प्रकार के मापदंडों के लिए अपने कोड में अतिरिक्त अतिरंजित कास्ट डाल रहा है ।

जब जरूरत नहीं हो तो एक एपीआई में बहुत से 'कास्ट' " रोते हुए भेड़िये " की तरह होते हैं, अंततः लोग 'कॉन्स्ट' को नजरअंदाज करना शुरू कर देंगे क्योंकि यह सभी जगह है और इसका मतलब है कि ज्यादातर समय कुछ भी नहीं है।

एपीआई में अतिरिक्त कास्ट्स के लिए "रिडक्टियो एड एब्सर्डम" तर्क इन पहले दो बिंदुओं के लिए अच्छा है यदि अधिक कॉन्स्ट पैरामीटर अच्छे हैं, तो हर तर्क जिसमें इस पर एक कॉस्ट हो सकती है, SHOULD का एक कॉस्ट है। वास्तव में, यदि यह सही मायने में अच्छा था, तो आप चाहते हैं कि आप मापदंडों के लिए डिफ़ॉल्ट हों और आपके पास "परिवर्तनशील" जैसे कीवर्ड हों, जब आप पैरामीटर बदलना चाहते हैं।

तो जहाँ भी हम कर सकते हैं वहाँ कास्ट में डालने की कोशिश करते हैं:

void mungerum(char * buffer, const char * mask, int count);

void mungerum(char * const buffer, const char * const mask, const int count);

ऊपर दिए गए कोड की लाइन पर विचार करें। न केवल घोषणा अधिक अव्यवस्थित और लंबी और पढ़ने में कठिन है, बल्कि चार में से तीन 'कास्ट' कीवर्ड को एपीआई उपयोगकर्ता द्वारा सुरक्षित रूप से अनदेखा किया जा सकता है। हालाँकि, 'कॉन्स्ट' के अतिरिक्त उपयोग ने दूसरी लाइन को संभावित रूप से खतरनाक बना दिया है !

क्यों?

पहले पैरामीटर का एक त्वरित गलत char * const bufferअर्थ आपको यह सोच सकता है कि यह डेटा बफर में मेमोरी को संशोधित नहीं करेगा जो कि - में पारित हो गया है, लेकिन यह सच नहीं है! सुपरफ्लस 'कास्ट' स्कैन या गलत तरीके से तेज़ी से फैलने पर आपके एपीआई के बारे में खतरनाक और गलत धारणाएं पैदा कर सकता है


एक कोड कार्यान्वयन स्टैंड-पॉइंट के साथ-साथ सुपरफ़्लुअस कॉस्ट खराब हैं:

#if FLEXIBLE_IMPLEMENTATION
       #define SUPERFLUOUS_CONST
#else
       #define SUPERFLUOUS_CONST             const
#endif

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count);

यदि FLEXIBLE_IMPLEMENTATION सही नहीं है, तो API "होनहार" है जो फ़ंक्शन को नीचे दिए गए तरीके से लागू नहीं करता है।

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count)
{
       // Will break if !FLEXIBLE_IMPLEMENTATION
       while(count--)
       {
              *dest++=*source++;
       }
}

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count)
{
       for(int i=0;i<count;i++)
       {
              dest[i]=source[i];
       }
}

यह एक बहुत ही मूर्खतापूर्ण वादा है। आपको ऐसा वादा क्यों करना चाहिए जो आपके कॉल करने वाले को कोई लाभ नहीं देता है और केवल आपके कार्यान्वयन को सीमित करता है?

ये दोनों एक ही फ़ंक्शन के पूरी तरह से मान्य कार्यान्वयन हैं हालांकि आपने जो कुछ भी किया है वह आपकी पीठ के पीछे अनावश्यक रूप से एक हाथ से बंधा हुआ है।

इसके अलावा, यह एक बहुत उथला वादा है जो आसानी से (और कानूनी रूप से दरकिनार) है।

inline void bytecopyWrapped(char * dest,
   const char *source, int count)
{
       while(count--)
       {
              *dest++=*source++;
       }
}
void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source,SUPERFLUOUS_CONST int count)
{
    bytecopyWrapped(dest, source, count);
}

देखिए, मैंने इसे वैसे भी लागू किया, भले ही मैंने वादा किया था कि - सिर्फ एक आवरण समारोह का उपयोग करके। यह तब होता है जब बुरा आदमी किसी फिल्म में किसी को नहीं मारने का वादा करता है और अपने गुर्गे को इसके बजाय उन्हें मारने का आदेश देता है।

उन अतिरंजित कास्ट एक फिल्म बुरे आदमी से एक वादा से अधिक के लायक नहीं हैं।


लेकिन झूठ बोलने की क्षमता और भी बदतर हो जाती है:

मुझे ज्ञान दिया गया है कि आप शीर्ष लेख (घोषणा) और कोड (परिभाषा) का उपयोग कर सकते हैं। कास्ट-हैप्पी अधिवक्ताओं का दावा है कि यह एक अच्छी बात है क्योंकि यह आपको केवल परिभाषा में कास्ट करने देता है।

// Example of const only in definition, not declaration
class foo { void test(int *pi); };
void foo::test(int * const pi) { }

हालाँकि, यह वाक्य सही है ... आप केवल घोषणा में एक स्पस्ट कॉन्स्ट डाल सकते हैं और परिभाषा में इसे अनदेखा कर सकते हैं। यह केवल एपीआई में एक भयानक बात और एक भयानक झूठ में अतिशयोक्तिपूर्ण कास्ट बनाता है - इस उदाहरण को देखें:

class foo
{
    void test(int * const pi);
};

void foo::test(int *pi) // Look, the const in the definition is so superfluous I can ignore it here
{
    pi++;  // I promised in my definition I wouldn't modify this
}

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

इस उदाहरण को देखें। कौन सा अधिक पठनीय है? क्या यह स्पष्ट है कि दूसरे फ़ंक्शन में अतिरिक्त चर का एकमात्र कारण यह है कि कुछ एपीआई डिजाइनर ने एक शानदार अंतर में फेंक दिया?

struct llist
{
    llist * next;
};

void walkllist(llist *plist)
{
    llist *pnext;
    while(plist)
    {
        pnext=plist->next;
        walk(plist);
        plist=pnext;    // This line wouldn't compile if plist was const
    }
}

void walkllist(llist * SUPERFLUOUS_CONST plist)
{
    llist * pnotconst=plist;
    llist *pnext;
    while(pnotconst)
    {
        pnext=pnotconst->next;
        walk(pnotconst);
        pnotconst=pnext;
    }
}

उम्मीद है कि हमने यहां कुछ सीखा है। सुपरफ्लस कांस्टेबल एक एपीआई-अव्यवस्थित आंखें, एक कष्टप्रद नाग, एक उथला और अर्थहीन वादा, एक अनावश्यक बाधा है, और कभी-कभी बहुत खतरनाक गलतियों की ओर जाता है।


9
क्यों घटता है? यदि आप एक संक्षिप्त टिप्पणी छोड़ दें तो यह बहुत अधिक उपयोगी है।
अदिसक

7
कॉन्स्ट तर्क का उपयोग करने का पूरा बिंदु चिह्नित लाइन को विफल करना है (plist = pnext)। यह फ़ंक्शन तर्क को अपरिवर्तनीय रखने के लिए एक उचित सुरक्षा उपाय है। मैं आपकी बात से सहमत हूं कि वे फ़ंक्शन घोषणाओं में खराब हैं (क्योंकि वे बहुत कम हैं), लेकिन वे कार्यान्वयन ब्लॉक में अपने उद्देश्यों की सेवा कर सकते हैं।
ताओ

23
@ Adisak मुझे आपके उत्तर के अनुसार कुछ भी गलत नहीं दिख रहा है, लेकिन यह आपकी टिप्पणियों से लगता है कि आप एक महत्वपूर्ण बिंदु को याद कर रहे हैं। फ़ंक्शन की परिभाषा / कार्यान्वयन एपीआई का हिस्सा नहीं है, जो केवल फ़ंक्शन घोषणा है । जैसा कि आपने कहा है, कास्ट मापदंडों के साथ फ़ंक्शन घोषित करना व्यर्थ है और अव्यवस्था जोड़ता है। हालांकि एपीआई के उपयोगकर्ताओं को इसके कार्यान्वयन को देखने की आवश्यकता नहीं हो सकती है। इस बीच कार्यान्वयनकर्ता केवल स्पष्टता के लिए फ़ंक्शन परिभाषा में कुछ मापदंडों को कसने के लिए निर्धारित कर सकता है, जो पूरी तरह से ठीक है।
jw013

17
@ jw013 सही है, void foo(int)और void foo(const int)ठीक वही कार्य हैं, ओवरलोड नहीं हैं। ideone.com/npN4W4 ideone.com/tZav9R यहाँ का कास्ट केवल फ़ंक्शन बॉडी का कार्यान्वयन विवरण है, और इसका ओवरलोड रिज़ॉल्यूशन पर कोई प्रभाव नहीं है। घोषणा से बाहर कास्ट छोड़ दो, एक सुरक्षित और neast एपीआई के लिए, लेकिन परिभाषा में कास्ट डालो , अगर आप कॉपी किए गए मूल्य को संशोधित नहीं करने का इरादा रखते हैं।
ओकेटलिस्ट

3
@ Adisak मुझे पता है कि यह पुराना है, लेकिन मेरा मानना ​​है कि सार्वजनिक API के लिए सही उपयोग अन्य तरीके से होगा। इस तरह से इन्टर्नल पर काम करने वाले डेवलपर्स ऐसी गलतियाँ नहीं करते हैं, pi++जब वे करने वाले नहीं होते।
कॉफिएंडकोड

39

C ++ में const डिफ़ॉल्ट होना चाहिए था। ऐशे ही :

int i = 5 ; // i is a constant

var int i = 5 ; // i is a real variable

8
बिल्कुल मेरे विचार।
कांस्टेंटिन

24
C के साथ संगतता बहुत महत्वपूर्ण है, कम से कम उन लोगों के लिए जो C ++ को डिज़ाइन करते हैं, यहां तक ​​कि इस पर विचार करने के लिए।

4
दिलचस्प है, मैंने ऐसा कभी नहीं सोचा था।
दान

6
इसी तरह, unsignedC ++ में डिफ़ॉल्ट होना चाहिए था। इस तरह: int i = 5; // i is unsignedऔर signed int i = 5; // i is signed
hkBattousai

25

जब मैंने एक जीवित के लिए C ++ को कोडित किया तो मैंने वह सब कुछ किया जो मैं संभवतः कर सकता था। कंपाइलर का उपयोग करना कंपाइलर की मदद करने का एक शानदार तरीका है। उदाहरण के लिए, अपने तरीके से वापस जाने वाले मानों को आप टाइपो से बचा सकते हैं जैसे:

foo() = 42

जब आपका मतलब था:

foo() == 42

यदि foo () को गैर-कॉन्स्टेंस संदर्भ देने के लिए परिभाषित किया गया है:

int& foo() { /* ... */ }

संकलक खुशी से आपको फ़ंक्शन कॉल द्वारा लौटाए गए अस्थायी अस्थायी के लिए मान प्रदान करेगा। इसे बनाना:

const int& foo() { /* ... */ }

इस संभावना को खत्म कर देता है।


6
किस कंपाइलर ने वह काम किया? संकलित करने का प्रयास करते समय जीसीसी एक त्रुटि देता है foo() = 42: त्रुटि: असाइनमेंट के बाएं ऑपरेंड के रूप में आवश्यक
अंतराल

यह सिर्फ गलत है। foo () = 42 2 = 3 के समान है, अर्थात एक संकलक त्रुटि। और एक कास्ट लौटना पूरी तरह से व्यर्थ है। यह बिल्ट-इन टाइप के लिए कुछ भी नहीं करता है।
जोश

2
मैंने कॉन्स्ट के इस उपयोग का सामना किया है और मैं आपको बता सकता हूं, अंत में यह लाभ की तुलना में अधिक परेशानी पैदा करता है। संकेत: const int foo()की तुलना में एक अलग प्रकार का है int foo(), जो आपको बड़ी परेशानी में लाता है यदि आप फ़ंक्शन पॉइंटर्स, सिग्नल / स्लॉट सिस्टम या बढ़ावा :: बाँध जैसी चीजों का उपयोग कर रहे हैं।
मेफेन

2
मैंने संदर्भ वापसी मूल्य को शामिल करने के लिए कोड को सही किया है।
अवधी

const int& foo()प्रभावी रूप से वैसा ही नहीं है int foo(), जैसे रिटर्न वैल्यू ऑप्टिमाइज़ेशन के कारण?
ज़ांटियर

15

Comp.lang.c ++ पर पुराने "द वीक ऑफ गुरु" लेख में इस विषय पर एक अच्छी चर्चा है

संबंधित GOTW लेख यहां हर्ब सटर की वेब साइट पर उपलब्ध है


1
हर्ब सटर वास्तव में स्मार्ट लड़का है :-) निश्चित रूप से पढ़ने लायक है और मैं उनकी सभी बातों से सहमत हूं।
अदिसक

2
अच्छा लेख, लेकिन मैं उनके तर्कों से असहमत हूं। मैं उन्हें कांस्टेबल बनाता हूं क्योंकि वे चर की तरह हैं, और मैं कभी नहीं चाहता कि कोई भी मेरे तर्कों में कोई बदलाव करे।
QBziZ

9

मैं कहता हूं कि अपने मूल्य मापदंडों को कस लें।

इस छोटी गाड़ी समारोह पर विचार करें:

bool isZero(int number)
{
  if (number = 0)  // whoops, should be number == 0
    return true;
  else
    return false;
}

यदि संख्या पैरामीटर कांस्टेबल था, तो कंपाइलर बंद हो जाएगा और हमें बग की चेतावनी देगा।


2
एक अन्य तरीका उपयोग कर रहा है अगर (0 == संख्या) ... और ...;
जोहान्स स्काउब -

5
@ क्रिसहुआंग-लीवर भयानक यह नहीं है, अगर आप योडा की तरह बोलते हैं: stackoverflow.com/a/2430307/210916
MPelletier

GCC / Clang -Wall आपको -Wententheses देता है, जो आपसे यह मांग करता है कि "if ((संख्या = 0))" यदि वह वास्तव में वही है जो आप करना चाहते हैं। जो योडा होने के विकल्प के रूप में अच्छी तरह से काम करता है।
जेट्स्की एस-टाइप

8

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

आपके उदाहरण में बूलियन बी पैरामीटर पर कास्ट को लागू करना केवल कार्यान्वयन पर एक बाधा डालता है और वर्ग के इंटरफ़ेस के लिए योगदान नहीं करता है (हालांकि आमतौर पर बदलते मापदंडों की सलाह नहीं दी जाती है)।

समारोह के लिए हस्ताक्षर

void foo(int a);

तथा

void foo(const int a);

वही है, जो आपके .c और .h की व्याख्या करता है

आसफ


6

यदि आप ->*या .*ऑपरेटरों का उपयोग करते हैं , तो यह बहुत जरूरी है।

यह आपको कुछ लिखने से रोकता है

void foo(Bar *p) { if (++p->*member > 0) { ... } }

जो मैंने लगभग अभी किया है, और जो संभवत: वह नहीं है जो आप करना चाहते हैं।

मैं क्या कहना चाहता था

void foo(Bar *p) { if (++(p->*member) > 0) { ... } }

और अगर मैंने constबीच में Bar *और डाल दिया था p, तो संकलक ने मुझे बताया होगा।


4
मैं तुरंत ऑपरेटर पूर्वता पर एक संदर्भ की जाँच कर रहा हूँ जब मैं एक साथ मिलाने वाला हूँ कि कई ऑपरेटर (यदि मैं पहले से ही 100% नहीं जानता), तो IMO यह एक गैर-मुद्दा है।
mk12

5

आह, एक कठिन। एक तरफ, एक घोषणा एक अनुबंध है और यह वास्तव में मूल्य से एक कांस्ट तर्क पारित करने के लिए समझ में नहीं आता है। दूसरी ओर, यदि आप फ़ंक्शन के क्रियान्वयन को देखते हैं, तो आप कंपाइलर को ऑप्टिमाइज़ करने के अधिक मौके देते हैं यदि आप किसी तर्क को स्थिर घोषित करते हैं।


5

जब आप कॉल करने वाले की वस्तु को संशोधित नहीं करेंगे, तो तर्क बेकार हो जाता है।

संदर्भ से गुजरते समय कास्ट को प्राथमिकता दी जानी चाहिए, जब तक कि फ़ंक्शन का उद्देश्य पारित मूल्य को संशोधित करना न हो।

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

int SomeClass::GetValue() const {return m_internalValue;}

यह उस ऑब्जेक्ट को संशोधित नहीं करने का वादा है जिस पर यह कॉल लागू होती है। दूसरे शब्दों में, आप कॉल कर सकते हैं:

const SomeClass* pSomeClass;
pSomeClass->GetValue();

यदि फ़ंक्शन कांस्टेबल नहीं था, तो इसका परिणाम संकलक चेतावनी में होगा।


5

मूल्य मानों को चिह्नित करना 'कॉन्स्ट' निश्चित रूप से एक व्यक्तिपरक बात है।

हालांकि मैं वास्तव में आपके उदाहरण की तरह, मूल्य मापदंडों कांस्ट को चिह्नित करना पसंद करता हूं।

void func(const int n, const long l) { /* ... */ }

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

एक छोटे से समारोह के लिए, यह यकीनन समय / स्थान की बर्बादी है कि वहां 'कास्ट' है, क्योंकि यह आमतौर पर बहुत स्पष्ट है कि तर्कों को फ़ंक्शन द्वारा संशोधित नहीं किया गया है।

हालांकि एक बड़े कार्य के लिए, इसके कार्यान्वयन प्रलेखन का एक रूप है, और यह संकलक द्वारा लागू किया जाता है।

मुझे यकीन है कि अगर मैं 'एन' और 'एल' के साथ कुछ गणना करता हूं, तो मैं एक अलग परिणाम प्राप्त करने के डर के बिना उस गणना को रिफ्लेक्टर / स्थानांतरित कर सकता हूं क्योंकि मैं एक जगह से चूक गया जहां एक या दोनों को बदल दिया जाता है।

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


4

हो सकता है कि यह एक मान्य तर्क न हो। लेकिन अगर हम एक फंक्शन कंपाइलर के अंदर एक कांस्टेबल वैरिएबल का मूल्य बढ़ाते हैं, तो हमें एक त्रुटि मिलेगी: " त्रुटि: रीड-ओनली पैरामीटर का वेतन वृद्धि "। तो इसका मतलब है कि हम कॉन्स्टेबल शब्द का इस्तेमाल गलती से अपने वेरिएबल्स को फंक्शन्स में मॉडिफाई करने से रोकने के लिए कर सकते हैं (जिसे हम केवल पढ़ने / पढ़ने के लिए नहीं करते हैं)। इसलिए अगर हमने गलती से कंपाइल टाइम कंपाइलर पर कर दिया तो हमें बताएंगे। यह विशेष रूप से महत्वपूर्ण है यदि आप केवल एक ही नहीं हैं जो इस परियोजना पर काम कर रहे हैं।


3

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

मुझे पता है कि सभी के लिए, कंपाइलर बहुत अच्छी तरह से एक कास्ट वैल्यू पैरामीटर देख सकता है, और कह सकता है, "अरे, यह फ़ंक्शन वैसे भी इसे संशोधित नहीं कर रहा है, इसलिए मैं संदर्भ से गुजर सकता हूं और कुछ घड़ी चक्रों को बचा सकता हूं।" मुझे नहीं लगता कि यह कभी ऐसा काम करेगा, क्योंकि यह फ़ंक्शन हस्ताक्षर को बदलता है, लेकिन यह बात बनाता है। शायद यह कुछ अलग स्टैक हेरफेर या कुछ और करता है ... बात यह है, मुझे नहीं पता, लेकिन मुझे पता है कि कंपाइलर की तुलना में होशियार होने की कोशिश करने से मुझे शर्म आती है।

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


हालांकि यह कुछ मामलों में मदद कर सकता है, मुझे संदेह है कि आशावाद को बढ़ावा देने की संभावना एक लाभ के रूप में नाटकीय रूप से बहुत अधिक है const। बल्कि, यह कार्यान्वयन के भीतर इरादे को स्पष्ट करने और बाद में थिंक को पकड़ने का मामला है (गलती से गलत स्थानीय चर बढ़ाना, क्योंकि यह नहीं था const)। समानांतर में, मैं यह भी जोड़ूंगा कि फ़ंक्शन हस्ताक्षर को बदलने के लिए कंपाइलर्स का बहुत स्वागत है, इस अर्थ में कि फ़ंक्शन को इनलाइन किया जा सकता है, और एक बार जब वे काम करते हैं तो पूरे तरीके से इनबिल्ट किए जा सकते हैं; संदर्भ जोड़ना या निकालना, 'वैरिएबल' को शाब्दिक बनाना, इत्यादि सभी इस नियम के अनुसार हैं
अंडरस्कोर_ड

3

1. मेरे आकलन के आधार पर सर्वश्रेष्ठ उत्तर:

मेरे मूल्यांकन के आधार पर @Adisak द्वारा उत्तर यहाँ सबसे अच्छा उत्तर है। ध्यान दें कि यह जवाब हिस्से में सबसे अच्छा है, क्योंकि यह भी है सबसे अच्छी तरह से बैक-अप वास्तविक कोड उदाहरण के साथ , ध्वनि और अच्छी तरह से सोच समझकर तर्क का उपयोग कर के अलावा।

2. मेरे अपने शब्द (सर्वोत्तम उत्तर से सहमत):

  1. पास-पास-मूल्य के लिए जोड़ने का कोई लाभ नहीं है const। यह सब है:
    1. कार्यान्वयनकर्ता को प्रतिलिपि बनाने के लिए हर बार जब वे स्रोत कोड में एक इनपुट परम को बदलना चाहते हैं (जो परिवर्तन वैसे भी कोई दुष्प्रभाव नहीं होगा क्योंकि जो कुछ भी पारित हो चुका है वह पहले से ही एक प्रतिलिपि है क्योंकि यह पास-बाय-वैल्यू है)। और अक्सर, इनपुट पारम को बदलना जो मूल्य द्वारा पारित किया जाता है, फ़ंक्शन को लागू करने के लिए उपयोग किया जाता है, इसलिए constहर जगह जोड़ना इस में बाधा बन सकता है।
    2. और constअनावश्यक रूप से कोड को constहर जगह एस के साथ जोड़ना , constएस से दूर ध्यान आकर्षित करना जो वास्तव में सुरक्षित कोड होना आवश्यक है।
  2. जब संकेत या संदर्भ के साथ काम करते हैं , हालांकि, constजरूरत पड़ने पर गंभीर रूप से महत्वपूर्ण होता है, और इसका उपयोग किया जाना चाहिए, क्योंकि यह फ़ंक्शन के बाहर लगातार परिवर्तन के साथ अवांछित दुष्प्रभावों को रोकता है , और इसलिए हर एक सूचक या संदर्भ का उपयोग constतब करना चाहिए जब केवल एक इनपुट हो। आउटपुट नहीं है। का प्रयोग const केवल संदर्भ या सूचक द्वारा पारित मानकों पर चलने से का अतिरिक्त लाभ है वास्तव में स्पष्ट है जो पैरामीटर संकेत या संदर्भ हैं। यह एक और बात है कि बाहर रहना और कहना "बाहर देखो! इसके साथ कोई भी परम constएक संदर्भ या सूचक है!"।
  3. मैंने जो ऊपर वर्णित किया है वह अक्सर उन पेशेवर सॉफ़्टवेयर संगठनों में प्राप्त सहमति है जो मैंने काम किया है और सबसे अच्छा अभ्यास माना जाता है। कभी-कभी, नियम भी सख्त किया गया है: "कभी भी उन मापदंडों पर कास्ट का उपयोग न करें, जो मूल्य द्वारा पारित किए जाते हैं, लेकिन हमेशा संदर्भ या सूचक द्वारा पारित मापदंडों पर इसका उपयोग करें यदि वे केवल इनपुट हैं।"

3. Google के शब्द (मेरे और सबसे अच्छे उत्तर के साथ सहमत):

(" Google C ++ स्टाइल गाइड " से)

मान द्वारा पास किए गए फ़ंक्शन पैरामीटर के लिए, कॉल पर कांस्ट का कोई प्रभाव नहीं होता है, इसलिए फ़ंक्शन घोषणाओं में इसकी अनुशंसा नहीं की जाती है। टोट्व # 109 देखें ।

स्थानीय चरों पर कास्ट का उपयोग न तो प्रोत्साहित किया जाता है और न ही हतोत्साहित किया जाता है।

स्रोत: Google C ++ स्टाइल गाइड का "कास्ट का उपयोग" खंड: https://google.github.io/styleguide/cppguide.html#Use_of_const । यह वास्तव में एक मूल्यवान खंड है, इसलिए पूरे खंड को पढ़ें।

ध्यान दें कि "TotW # 109" का अर्थ है "टिप ऑफ़ द वीक # 109: अर्थपूर्ण constफंक्शन डिक्लेरेशन" , और यह एक उपयोगी रीड भी है। यह क्या करना है पर अधिक जानकारीपूर्ण और कम प्रिस्क्रिपटिव है, और संदर्भ के आधार पर Google C ++ स्टाइल गाइड नियम से पहलेconst ऊपर उद्धृत किया गया था, लेकिन इसके द्वारा प्रदान की गई स्पष्टता के परिणामस्वरूप, constऊपर बताए गए नियम को Google ++ ++ में जोड़ा गया था शैली गाइड।

यह भी ध्यान दें कि भले ही मैं अपनी स्थिति के बचाव में यहां Google C ++ स्टाइल गाइड को उद्धृत कर रहा हूं, लेकिन इसका यह मतलब नहीं है कि मैं हमेशा गाइड का पालन करता हूं या हमेशा गाइड का पालन करने की सलाह देता हूं। वे जिन चीजों की सलाह देते हैं उनमें से कुछ सीधे सादे अजीब हैं, जैसे कि "कॉन्स्टेंट नेम्स" के लिए उनका kDaysInAWeekनामकरण सम्मेलनहालांकि, यह अभी भी इंगित करने के लिए उपयोगी और प्रासंगिक है जब दुनिया की सबसे सफल और प्रभावशाली तकनीकी और सॉफ्टवेयर कंपनियों में से एक ही औचित्य का उपयोग करता है जैसा कि मैं और अन्य @ @ जैसे लोग इस मामले पर हमारे दृष्टिकोण का समर्थन करने के लिए करते हैं।

4. क्लैंग के लिंटर, clang-tidyके पास इसके लिए कुछ विकल्प हैं:

ए। यह भी ध्यान देने योग्य है कि क्लैंग के लिंटर, clang-tidyके पास एक विकल्प है readability-avoid-const-params-in-decls, जो यहां वर्णित है , पास कोड -वैल्यू फ़ंक्शन मापदंडों के लिए उपयोग करने वाले कोड बेस में लागू करने के constलिए :

जाँचता है कि क्या फ़ंक्शन की घोषणा में पैरामीटर हैं जो शीर्ष स्तर का कॉन्स्टेंस हैं।

घोषणाओं में कास्ट वैल्यू किसी फ़ंक्शन के हस्ताक्षर को प्रभावित नहीं करते हैं, इसलिए उन्हें वहां नहीं रखा जाना चाहिए।

उदाहरण:

void f(const string);   // Bad: const is top level.
void f(const string&);  // Good: const is not top level.

और यहां दो और उदाहरण हैं जो मैं खुद को पूर्णता और स्पष्टता के लिए जोड़ रहा हूं:

void f(char * const c_string);   // Bad: const is top level. [This makes the _pointer itself_, NOT what it points to, const]
void f(const char * c_string);   // Good: const is not top level. [This makes what is being _pointed to_ const]

B. इसका यह विकल्प भी है: readability-const-return-type- https://clang.llvm.org/extra/clang-tidy/checks/readability-const-return-type.html

5. मैं इस मामले पर एक शैली गाइड शब्द कैसे कहूँगा के लिए मेरा व्यावहारिक दृष्टिकोण:

मैं इसे अपनी स्टाइल गाइड में बस कॉपी और पेस्ट करूंगा:

[COPY / PASTE START]

  1. हमेशा const संदर्भ या पॉइंटर द्वारा पारित फ़ंक्शन मापदंडों पर उपयोग करें जब उनकी सामग्री (वे जो इंगित करते हैं) को बदलने का इरादा नहीं है। इस तरह, यह स्पष्ट हो जाता है जब संदर्भ या सूचक आईएस द्वारा पारित एक चर को बदलने की उम्मीद की जाती है, क्योंकि इसमें कमी होगी const। इस उपयोग के मामले constमें फ़ंक्शन के बाहर आकस्मिक साइड इफेक्ट को रोकता है।
  2. यह है की सिफारिश नहीं उपयोग करने के लिए constसमारोह मापदंडों मूल्य द्वारा पारित पर है, क्योंकि constफोन करने वाले पर कोई प्रभाव नहीं: भले ही चर समारोह में परिवर्तित हो जाता है वहाँ समारोह के बाहर कोई साइड इफेक्ट हो जाएगा। अतिरिक्त औचित्य और अंतर्दृष्टि के लिए निम्नलिखित संसाधन देखें:
    1. "Google C ++ स्टाइल गाइड" "कांस्ट का उपयोग" अनुभाग
    2. "सप्ताह की टिप # 109: constफ़ंक्शन घोषणाओं में सार्थक "
    3. "फंक्शन पैरामीटर्स के लिए 'कास्ट' का उपयोग करने पर एडिसक का स्टैक ओवरफ्लो जवाब"
  3. " घोषणाओं में फ़ंक्शन मापदंडों पर शीर्ष-स्तर [अर्थात: मान से पारित मापदंडों पर ] का उपयोग कभी न करें जो कि परिभाषाएँ नहीं हैं (और व्यर्थ की नकल / पेस्ट न करें) सावधान रहें । यह अर्थहीन है और संकलक द्वारा अनदेखा किया गया है, यह दृश्य शोर है। , और यह पाठकों को गुमराह कर सकता है "( https://abseil.io/tips/109 , जोर जोड़ा)। constconstconst
    1. constसंकलन पर प्रभाव डालने वाले एकमात्र क्वालीफायर फ़ंक्शन फ़ंक्शन में रखे जाते हैं, फ़ंक्शन के आगे की घोषणा में नहीं, जैसे कि किसी हेडर फ़ाइल में फ़ंक्शन (विधि) घोषणा में।
  4. किसी फ़ंक्शन द्वारा लौटाए गए मानों पर कभी भी शीर्ष-स्तरीय const[अर्थात: मूल्य से पारितconst चर पर] का उपयोग न करें
  5. constकिसी फ़ंक्शन द्वारा दिए गए पॉइंटर्स या संदर्भों का उपयोग करना कार्यान्वयनकर्ता पर निर्भर है , क्योंकि यह कभी-कभी उपयोगी होता है।
  6. TODO: उपरोक्त clang-tidyविकल्पों में से कुछ को लागू करता है :
    1. https://clang.llvm.org/extra/clang-tidy/checks/readability-avoid-const-params-in-decls.html
    2. https://clang.llvm.org/extra/clang-tidy/checks/readability-const-return-type.html

constऊपर वर्णित नियमों को प्रदर्शित करने के लिए यहां कुछ कोड उदाहरण दिए गए हैं:

constपैरामीटर उदाहरण:
(कुछ यहाँ से उधार लिए गए हैं )

void f(const std::string);   // Bad: const is top level.
void f(const std::string&);  // Good: const is not top level.

void f(char * const c_string);   // Bad: const is top level. [This makes the _pointer itself_, NOT what it points to, const]
void f(const char * c_string);   // Good: const is not top level. [This makes what is being _pointed to_ const]

constवापसी प्रकार के उदाहरण:
(कुछ यहाँ से उधार लिए गए हैं )

// BAD--do not do this:
const int foo();
const Clazz foo();
Clazz *const foo();

// OK--up to the implementer:
const int* foo();
const int& foo();
const Clazz* foo();

[COPY / PASTE END]


2

आपके द्वारा उल्लिखित मामले में, यह आपके एपीआई के कॉलर्स को प्रभावित नहीं करता है, यही कारण है कि यह आमतौर पर नहीं किया जाता है (और हेडर में आवश्यक नहीं है)। यह केवल आपके फ़ंक्शन के कार्यान्वयन को प्रभावित करता है।

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


2

मैं मूल्य-पास पैरामीयर के लिए कास्ट का उपयोग नहीं करता हूं। कॉलर को परवाह नहीं है कि आप पैरामीटर को संशोधित करते हैं या नहीं, यह एक कार्यान्वयन विवरण है।

यदि वे अपनी आवृत्ति को संशोधित नहीं करते हैं, तो उन्हें कब्जे के रूप में चिह्नित करना वास्तव में महत्वपूर्ण है। जैसा कि आप जाते हैं, ऐसा करें, क्योंकि अन्यथा आप बहुत सारे const_cast के साथ समाप्त हो सकते हैं <> या हो सकता है कि आपको विधि कॉन्स्टेबल को चिह्नित करने के लिए बहुत सारे कोड बदलने की आवश्यकता हो क्योंकि यह अन्य तरीकों को कॉल करता है जिन्हें कॉस्ट के रूप में चिह्नित किया जाना चाहिए था।

यदि मुझे उन्हें संशोधित करने की आवश्यकता नहीं है, तो मैं स्थानीय var कॉन्स्ट का चिह्न लगाता हूं। मेरा मानना ​​है कि यह "चलती भागों" की पहचान करना आसान बनाकर कोड को समझना आसान बनाता है।



2

मैं कांस्ट का उपयोग करता हूं मैं कर सकता था। मापदंडों के लिए कास्ट का मतलब है कि उन्हें अपना मूल्य नहीं बदलना चाहिए। संदर्भ से गुजरते समय यह विशेष रूप से मूल्यवान है। फंक्शन के लिए कॉन्स्ट घोषित करता है कि फ़ंक्शन को क्लास के सदस्यों को नहीं बदलना चाहिए।


2

संक्षेप में:

  • "आम तौर पर पास-दर-मूल्य अप्रयुक्त और सबसे अच्छा भ्रामक है।" से GOTW006
  • लेकिन आप उन्हें .cpp में जोड़ सकते हैं जैसा कि आप चर के साथ करेंगे।
  • ध्यान दें कि मानक लायब्रेरी का उपयोग नहीं करता है। जैसे std::vector::at(size_type pos)। मानक पुस्तकालय के लिए क्या अच्छा है मेरे लिए अच्छा है।

2
"मानक पुस्तकालय के लिए क्या अच्छा है मेरे लिए अच्छा है" हमेशा सच नहीं होता है। उदाहरण के लिए, मानक पुस्तकालय _Tmpहर समय की तरह बदसूरत चर नामों का उपयोग करता है - आप ऐसा नहीं चाहते हैं (वास्तव में आपको उनका उपयोग करने की अनुमति नहीं है)।
अनातोलीग जूल

1
@anatolyg यह एक कार्यान्वयन विवरण है
फर्नांडो

2
ठीक है, तर्क सूची में चर नाम और कास्ट-योग्य दोनों प्रकार के कार्यान्वयन विवरण हैं। मैं जो कहना चाहता हूं, वह यह है कि मानक पुस्तकालय का कार्यान्वयन कभी-कभी अच्छा नहीं होता है। कभी-कभी, आप बेहतर कर सकते हैं (और करना चाहिए)। 10 साल पहले मानक पुस्तकालय के लिए कोड कब लिखा गया था? 5 साल पहले (इसके कुछ नए हिस्से)? हम आज बेहतर कोड लिख सकते हैं।
अनातोलीग

1

यदि पैरामीटर मान द्वारा पारित किया जाता है (और एक संदर्भ नहीं है), आमतौर पर बहुत अंतर नहीं होता है कि पैरामीटर को कास्ट के रूप में घोषित किया गया है या नहीं (जब तक कि इसमें संदर्भ सदस्य नहीं है - अंतर्निहित प्रकारों के लिए कोई समस्या नहीं है)। यदि पैरामीटर एक संदर्भ या पॉइंटर है, तो आमतौर पर संदर्भित / पॉइंट-इन मेमोरी की रक्षा करना बेहतर होता है, न कि पॉइंटर ही (मुझे लगता है कि आप संदर्भ को स्वयं नहीं बना सकते हैं, यह नहीं कि यह बहुत मायने रखता है क्योंकि आप रेफरी को बदल नहीं सकते हैं) । यह एक अच्छा विचार है कि आप जो कुछ भी कर सकते हैं उसे संरक्षित करना एक अच्छा विचार है। आप एक गलती करने के डर के बिना इसे छोड़ सकते हैं यदि पैरामीटर सिर्फ PODs (बिल्ट-इन प्रकार सहित) हैं और उनमें से आगे सड़क के साथ बदलने का कोई मौका नहीं है (जैसे आपके उदाहरण में बूल पैरामीटर)।

मैं .h / .cpp फ़ाइल घोषणा अंतर के बारे में नहीं जानता था, लेकिन यह कुछ अर्थ रखता है। मशीन कोड स्तर पर, कुछ भी "कॉन्स्ट" नहीं है, इसलिए यदि आप किसी फ़ंक्शन (.h) को गैर-कॉन्स्टेंस घोषित करते हैं, तो कोड वैसा ही होता है जैसे कि आप इसे कॉन्स्टेंस (एक तरफ अनुकूलन) घोषित करते हैं। हालांकि, यह आपको कंपाइलर को सूचीबद्ध करने में मदद करता है कि आप फ़ंक्शन (.ccp) के कार्यान्वयन के अंदर चर के मूल्य को नहीं बदलेंगे। यह उस स्थिति में उपयोगी हो सकता है जब आप एक ऐसे इंटरफ़ेस से विरासत में प्राप्त कर रहे हों जो बदलाव की अनुमति देता है, लेकिन आपको आवश्यक कार्यक्षमता प्राप्त करने के लिए पैरामीटर को बदलने की आवश्यकता नहीं है।


0

मैं इस तरह के मापदंडों पर कसना नहीं करूंगा - हर कोई पहले से ही जानता है कि एक बूलियन (बूलियन के विपरीत) स्थिर है, इसलिए इसे जोड़ने से लोगों को लगता है कि "रुको, क्या होगा?" या यहां तक ​​कि आप संदर्भ द्वारा पैरामीटर पारित कर रहे हैं।


4
कभी-कभी आप किसी ऑब्जेक्ट को संदर्भ (प्रदर्शन कारणों से) के लिए पास करना चाहते हैं, लेकिन इसे नहीं बदलते हैं, इसलिए तब कांस्ट अनिवार्य है। इस तरह के सभी मापदंडों - यहां तक ​​कि बूल - कॉन्स्ट को रखना अच्छा अभ्यास होगा, जिससे आपके कोड को पढ़ना आसान हो जाएगा।
gbjbaanb

0

कॉन्स्ट के साथ याद रखने वाली बात यह है कि चीजों को शुरुआत से ही बनाना ज्यादा आसान है, जितना कि उन्हें बाद में करने की कोशिश करना।

जब आप किसी चीज को अपरिवर्तित करना चाहते हैं तो कॉस्ट का उपयोग करें - इसका एक जोड़ा संकेत है जो बताता है कि आपका कार्य क्या करता है और क्या उम्मीद करता है। मैंने कई सी एपीआई देखे हैं जो उनमें से कुछ के साथ कर सकते हैं, विशेष रूप से वे जो सी-स्ट्रिंग्स को स्वीकार करते हैं!

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


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

@ डॉनचैच 8 साल बाद, वाह। वैसे भी, जैसा कि ओपी ने कहा "मुझे यह जानकर आश्चर्य हुआ कि आप एक फ़ंक्शन घोषणा में मापदंडों से कसना छोड़ सकते हैं लेकिन इसे फ़ंक्शन परिभाषा में शामिल कर सकते हैं"।
gbjbaanb

0

वैल्यू-पैरामीटर "कॉन्स्ट" बनाने का वास्तव में कोई कारण नहीं है क्योंकि फ़ंक्शन केवल चर की एक प्रति को संशोधित कर सकता है।

"कॉन्स्ट" का उपयोग करने का कारण यह है कि यदि आप कुछ बड़ा (उदाहरण के लिए सदस्यों के साथ एक संरचना) पास कर रहे हैं, तो इस स्थिति में यह सुनिश्चित करता है कि फ़ंक्शन इसे संशोधित नहीं कर सकता है; यदि आप इसे पारंपरिक तरीके से संशोधित करने का प्रयास करते हैं, तो कंपाइलर शिकायत करेगा। यह गलती से संशोधित होने से रोकता है।


0

कॉन्स्ट पैरामीटर केवल तब उपयोगी होता है जब पैरामीटर संदर्भ द्वारा पारित किया जाता है, या तो संदर्भ या पॉइंटर। जब कंपाइलर एक कास्ट पैरामीटर देखता है, तो यह सुनिश्चित करता है कि पैरामीटर में उपयोग किया जाने वाला चर फ़ंक्शन के शरीर के भीतर संशोधित नहीं किया गया है। कोई क्यों मान को एक स्थिर मान बनाना चाहेगा? :-)


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

0

जैसा कि मान द्वारा मानदंड पास किए जा रहे हैं, इससे कॉल फ़ंक्शंस के परिप्रेक्ष्य से कास्ट या नहीं निर्दिष्ट करने पर कोई फर्क नहीं पड़ता। मूल रूप से मान को पास मान द्वारा पास घोषित करने का कोई मतलब नहीं है।


0

आपके उदाहरणों में सभी बाधाओं का कोई उद्देश्य नहीं है। C ++ डिफ़ॉल्ट रूप से पास-बाय-वैल्यू है, इसलिए फ़ंक्शन को उन चींटियों और बूलियन की प्रतियां मिलती हैं। भले ही फ़ंक्शन उन्हें संशोधित करता है, लेकिन कॉलर की प्रतिलिपि प्रभावित नहीं होती है।

इसलिए मैं अतिरिक्त बाधाओं से बचूंगा क्योंकि

  • वे निरर्थक हैं
  • वे पाठ को अव्यवस्थित करते हैं
  • वे मुझे उन मामलों में मूल्य को बदलने से रोकते हैं जहां यह उपयोगी या कुशल हो सकता है।

-1

मुझे पता है कि प्रश्न "थोड़ा सा" पुराना है, लेकिन जैसा कि मैंने इसे प्राप्त किया है कि कोई और भविष्य में भी ऐसा कर सकता है ... ... फिर भी मुझे संदेह है कि गरीब साथी मेरी टिप्पणी पढ़ने के लिए यहां नीचे सूचीबद्ध करेंगे :)

ऐसा लगता है कि हम अभी भी सी-स्टाइल के सोचने के तरीके तक ही सीमित हैं। OOP प्रतिमान में हम वस्तुओं के साथ खेलते हैं, न कि प्रकार। कॉन्स्ट ऑब्जेक्ट गैर-कॉस्ट ऑब्जेक्ट से वैचारिक रूप से भिन्न हो सकता है, विशेष रूप से तार्किक-कॉन्स्टेंस के अर्थ में (बिटवाइज़-कॉन्स के विपरीत)। इस प्रकार भले ही फ़ंक्शन परम की सही शुद्धता (शायद) PODs के मामले में अति-सावधानी हो, वस्तुओं के मामले में ऐसा नहीं है। यदि कोई फ़ंक्शन किसी कॉस्ट ऑब्जेक्ट के साथ काम करता है तो उसे ऐसा कहना चाहिए। निम्नलिखित कोड स्निपेट पर विचार करें

#include <iostream>

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class SharedBuffer {
private:

  int fakeData;

  int const & Get_(int i) const
  {

    std::cout << "Accessing buffer element" << std::endl;
    return fakeData;

  }

public:

  int & operator[](int i)
  {

    Unique();
    return const_cast<int &>(Get_(i));

  }

  int const & operator[](int i) const
  {

    return Get_(i);

  }

  void Unique()
  {

    std::cout << "Making buffer unique (expensive operation)" << std::endl;

  }

};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void NonConstF(SharedBuffer x)
{

  x[0] = 1;

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ConstF(const SharedBuffer x)
{

  int q = x[0];

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int main()
{

  SharedBuffer x;

  NonConstF(x);

  std::cout << std::endl;

  ConstF(x);

  return 0;

}

ps .: आप यह तर्क दे सकते हैं कि (कॉन्स्टेबल) संदर्भ यहां अधिक उपयुक्त होगा और आपको वही व्यवहार देगा। ठीक है, ठीक है। बस एक अलग तस्वीर दे रहा हूँ जो मैं कहीं और देख सकता था ...

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