c++ क्या हेडर में फ़ंक्शन मापदंडों के रूप में उपयोग किए जाने वाले "आदिम" प्रकारों के सामने "कॉन्स्ट" निकालना बेहतर है?




header const (5)

API परिप्रेक्ष्य से, जैसा कि कई अन्य लोगों ने उत्तर दिया है, निम्नलिखित सभी समतुल्य हैं, और अधिभार-संकल्प के लिए समान हैं:

void foo( int );
void foo( const int );

लेकिन एक बेहतर सवाल यह है कि क्या यह इस एपीआई के उपभोक्ता को कोई अर्थपरक अर्थ प्रदान करता है या नहीं, या क्या यह कार्यान्वयन के एक डेवलपर से अच्छे व्यवहार का कोई प्रवर्तन प्रदान करता है या नहीं।

किसी भी अच्छी तरह से परिभाषित डेवलपर कोडिंग दिशानिर्देशों के बिना जो स्पष्ट रूप से इसे परिभाषित करते हैं, const स्केलर तर्कों का कोई स्पष्ट स्पष्ट अर्थ अर्थ नहीं है

एक उपभोक्ता से: const int आपके इनपुट को नहीं बदलता है। यह अभी भी एक शाब्दिक हो सकता है, या यह दूसरे चर से हो सकता है (दोनों const या नॉन- const )

डेवलपर से: const int किसी चर की स्थानीय प्रति (इस मामले में, एक फ़ंक्शन तर्क) पर प्रतिबंध लगाता है। यह केवल तर्क को संशोधित करने का मतलब है, आप चर की एक और प्रति लेते हैं और इसके बजाय इसे संशोधित करते हैं।

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

तर्क को const रूप में चिह्नित करने से, इसका const मतलब है कि इस प्रति को संशोधित नहीं किया जा सकता है; लेकिन यह डेवलपर को इसे कॉपी करने और इस कॉपी में संशोधन करने से नहीं रोकता है। चूँकि यह शुरुआत से एक प्रति थी, इसलिए यह कार्यान्वयन के अंदर से यह सब लागू नहीं करता है - और अंततः उपभोक्ता के दृष्टिकोण से बहुत अंतर नहीं पड़ता है।

यह संदर्भ से गुजरने के विपरीत है, जिसमें int& का संदर्भ const int& से const int& से अलग है। पूर्व अपने इनपुट को बदलने में सक्षम है; उत्तरार्द्ध केवल इनपुट का अवलोकन करने में सक्षम है (बशर्ते कि कार्यान्वयन const_cast को दूर नहीं करता है - लेकिन इस संभावना को अनदेखा करता है); इस प्रकार, संदर्भों में कमी का अर्थ निहित अर्थ है।

यह सार्वजनिक एपीआई में होने का अधिक लाभ प्रदान नहीं करता है; और (imo) कार्यान्वयन में अनावश्यक प्रतिबंधों का परिचय देता है। एक मनमाना, आकस्मिक उदाहरण के रूप में - जैसे एक साधारण कार्य:

void do_n_times( int n )
{
   while( n-- > 0 ) {
       // do something n times
   } 
}

अब एक अनावश्यक कॉपी का उपयोग करके लिखना होगा:

void do_n_times( const int n )
{
    auto n_copy = n;
    while( n_copy-- > 0 ) {
        // do something n times
    }
}

सार्वजनिक एपीआई में भले ही कास्ट स्केलर्स का उपयोग किया जाता है, एक महत्वपूर्ण बात यह है कि डिजाइन के अनुरूप होना चाहिए। यदि एपीआई नॉन- const स्केलरों का उपयोग करने के लिए const स्केलर तर्कों का उपयोग करने के बीच बेतरतीब ढंग से स्विच करता है, तो यह भ्रम पैदा कर सकता है कि क्या उपभोक्ता के लिए कोई निहित अर्थ है।

TL; DR: एक सार्वजनिक एपीआई में const स्केलर टाइप्स शब्दार्थ अर्थ को व्यक्त नहीं करते हैं जब तक कि आपके डोमेन के लिए आपके स्वयं के दिशानिर्देशों द्वारा स्पष्ट रूप से परिभाषित नहीं किया गया हो।

https://code.i-harness.com

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

निम्नलिखित उदाहरण है।

example.h

int ProcessScore(const int score);

example.cc

int ProcessScore(const int score) {
  // Do some calculation using score
  return some_value;
}

उनका सुझाव निम्नानुसार है:

example.h

int ProcessScore(int score);  // const is removed here.

example.cc

int ProcessScore(const int score) {
  // Do some calculation using score
  return some_value;
}

लेकिन मैं कुछ उलझन में हूँ। आमतौर पर, उपयोगकर्ता केवल हेडर को देखेगा, इसलिए यदि हेडर और स्रोत फ़ाइल के बीच असंगतता है, तो यह भ्रम पैदा कर सकता है।

क्या कोई इस पर कुछ सलाह दे सकता है?


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

void f(int);
void f(const int);

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

यहाँ मानक से सटीक उद्धरण है।

पैरामीटर प्रकारों की सूची तैयार करने के बाद, फ़ंक्शन प्रकार बनाते समय पैरामीटर प्रकार को संशोधित करने वाले किसी भी शीर्ष-स्तरीय cv-क्वालिफायर को हटा दिया जाता है। रूपांतरित पैरामीटर प्रकारों की परिणामी सूची और दीर्घवृत्त या फ़ंक्शन पैरामीटर पैक की उपस्थिति या अनुपस्थिति, फ़ंक्शन का पैरामीटर-टाइप-सूची है। [नोट: यह परिवर्तन मापदंडों के प्रकारों को प्रभावित नहीं करता है। उदाहरण के लिए, int(*)(const int p, decltype(p)*) और int(*)(int, const int*) समान प्रकार हैं। - अंतिम नोट]

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


कोड समीक्षा में आपको दी गई सिफारिशों का पालन करें।

मूल्य तर्क के लिए const का उपयोग करने का कोई शब्दार्थक मूल्य नहीं है - यह आपके कार्य के कार्यान्वयन के लिए केवल सार्थक (संभावित) है - और यहां तक ​​कि उस स्थिति में भी मैं यह तर्क दूंगा कि यह अनावश्यक है।

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

int a = 7;
do_something( a );

void do_something(       int& x );  // 'a' may be modified
void do_something( const int& x );  // I will not modify 'a'
void do_something(       int  x );  // no one cares what happens to x

const का उपयोग करना TMI के समान कुछ है - यह फ़ंक्शन के अंदर के अलावा कहीं भी महत्वहीन है कि 'x' को संशोधित किया गया है या नहीं।

edit2: मैं भी स्टोरीटेलर के जवाब में जानकारी को बहुत पसंद करता हूं


मैंने सोचा था कि const कंपाइलर के लिए एक संकेत है कि कुछ अभिव्यक्तियाँ नहीं बदलती हैं और तदनुसार अनुकूलन करने के लिए। उदाहरण के लिए, मैं परीक्षण कर रहा था कि क्या संख्या संख्या के वर्गमूल तक विभाजकों की तलाश करके प्रमुख है और मैंने सोचा कि तर्क की घोषणा करते हुए लूप के for sqrt(n) ले जाएगा, लेकिन यह नहीं हुआ।

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


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

void foo(int const i, int const j);
void foo(int i, int const j);
void foo(int const i, int j);
void foo(int i, int j);

हालांकि, कांस्टेबल को फंक्शन बॉडी के अंदर नजरअंदाज नहीं किया जाता है। वहाँ यह कब्ज की शुद्धता पर प्रभाव डाल सकता है। लेकिन यह फ़ंक्शन का कार्यान्वयन विवरण है। तो आम सहमति यह है:

  1. घोषणा से बाहर कास्ट छोड़ दें। यह सिर्फ अव्यवस्था है, और यह प्रभावित नहीं करता है कि ग्राहक फ़ंक्शन को कैसे कॉल करेंगे।

  2. यदि आप कंपाइलर को पैरामीटर के किसी भी आकस्मिक संशोधन को पकड़ने की इच्छा रखते हैं, तो परिभाषा में कास्ट छोड़ दें।







primitive-types