c++ समारोह सूचक, कांस्ट तर्क शुद्धता के लिए फ़ंक्शन असाइन करना?




parameters const (3)

मैं अपने विश्वविद्यालय में अब C ++ और OOP की मूल बातें सीख रहा हूं। मुझे 100% यकीन नहीं है कि एक फ़ंक्शन पॉइंटर कैसे काम करता है जब उन्हें कार्य सौंपते हैं। मुझे निम्नलिखित कोड का सामना करना पड़ा:

void mystery7(int a, const double b) { cout << "mystery7" << endl; }
const int mystery8(int a, double b) { cout << "mystery8" << endl; }

int main() {
    void(*p1)(int, double) = mystery7;            /* No error! */
    void(*p2)(int, const double) = mystery7;
    const int(*p3)(int, double) = mystery8;
    const int(*p4)(const int, double) = mystery8; /* No error! */
}

मेरी समझ से, p2 और p3 असाइनमेंट ठीक हैं क्योंकि फंक्शन पैरामीटर टाइप मैच और कॉन्स्ट-नेस सही है। लेकिन p1 और p4 असाइनमेंट विफल क्यों नहीं होते? क्या यह कॉन्स्टेबल डबल / int से नॉन-कास्ट डबल / int मैच करना गैरकानूनी नहीं होना चाहिए?

https://code.i-harness.com


C ++ मानक के अनुसार (C ++ 17, 16.1 अधिभार घोषित)

(३.४) - पैरामीटर घोषणाएँ जो केवल कॉन्स्टेंस की मौजूदगी या अनुपस्थिति में भिन्न होती हैं और / या अस्थिर होती हैं। अर्थात्, प्रत्येक पैरामीटर प्रकार के लिए कास्ट और वाष्पशील प्रकार-निर्दिष्टकर्ताओं को अनदेखा किया जाता है जब यह निर्धारित किया जाता है कि किस फ़ंक्शन को घोषित, परिभाषित या कहा जा रहा है।

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

void mystery7(int a, const double b);

और फ़ंक्शन प्रकार void( int, double )

साथ ही निम्नलिखित फ़ंक्शन घोषणा पर विचार करें

void f( const int * const p );

यह निम्नलिखित घोषणा के बराबर है

void f( const int * p );

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

इस बात पर ध्यान दें कि हालांकि C ++ स्टैंडर्ड में "कॉन्स्ट रेफरेंस" शब्द का इस्तेमाल किया गया है, लेकिन यह स्वयं पॉइंटर्स के विपरीत नहीं हो सकता है। वह निम्नलिखित घोषणा है

int & const x = initializer;

गलत है।

जबकि यह घोषणा है

int * const x = initializer;

सही है और एक निरंतर सूचक घोषित करता है।


एक ऐसी स्थिति है जहां एक फ़ंक्शन तर्क में एक const को जोड़ना या हटाना एक गंभीर बग है। यह तब होता है जब आप सूचक द्वारा तर्क पास करते हैं।

यहाँ एक सरल उदाहरण है कि क्या गलत हो सकता है। यह कोड C में टूट गया है:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// char * strncpy ( char * destination, const char * source, size_t num );

/* Undeclare the macro required by the C standard, to get a function name that
 * we can assign to a pointer:
 */
#undef strncpy

// The correct declaration:
char* (*const fp1)(char*, const char*, size_t) = strncpy;
// Changing const char* to char* will give a warning:
char* (*const fp2)(char*, char*, size_t) = strncpy;
// Adding a const qualifier is actually dangerous:
char* (*const fp3)(const char*, const char*, size_t) = strncpy;

const char* const unmodifiable = "hello, world!";

int main(void)
{
  // This is undefined behavior:
  fp3( unmodifiable, "Whoops!", sizeof(unmodifiable) );

  fputs( unmodifiable, stdout );
  return EXIT_SUCCESS;
}

यहाँ समस्या fp3 साथ है। यह एक फ़ंक्शन के लिए एक पॉइंटर है जो दो const char* तर्कों को स्वीकार करता है। हालाँकि, यह मानक लाइब्रेरी कॉल strncpy() py की ओर इशारा करता है, जिसका पहला तर्क एक बफर है जिसे यह संशोधित करता है । यही है, fp3( dest, src, length ) में एक प्रकार है जो डेटा fp3( dest, src, length ) को संशोधित नहीं करने का वादा करता है, लेकिन फिर वह strncpy() पर तर्क पारित करता है, जो उस डेटा को संशोधित करता है! यह केवल इसलिए संभव है क्योंकि हमने फ़ंक्शन के प्रकार हस्ताक्षर को बदल दिया है।

एक स्ट्रिंग स्थिरांक को संशोधित करने की कोशिश करना अपरिभाषित व्यवहार है - हमने प्रभावी रूप से प्रोग्राम को strncpy( "hello, world!", "Whoops!", sizeof("hello, world!") ) को कॉल करने के लिए कहा था - और मैंने अपने विभिन्न कंपाइलरों पर परीक्षण किया। , यह रनटाइम पर चुपचाप विफल हो जाएगा।

किसी भी आधुनिक सी कंपाइलर को fp1 को असाइनमेंट की अनुमति देनी चाहिए लेकिन आपको चेतावनी दी जाती है कि आप पैर में fp2 या fp3 साथ अपने आप को शूट कर रहे हैं। C ++ में, fp2 और fp3 लाइनें बिना fp3 बिना संकलित नहीं होंगी। स्पष्ट कलाकारों को जोड़ने से कंपाइलर को पता चलता है कि आप जानते हैं कि आप क्या कर रहे हैं और चेतावनियों को चुप कर देते हैं, लेकिन कार्यक्रम अभी भी अपने अपरिभाषित व्यवहार के कारण विफल रहता है।

const auto fp2 =
  reinterpret_cast<char*(*)(char*, char*, size_t)>(strncpy);
// Adding a const qualifier is actually dangerous:
const auto fp3 =
  reinterpret_cast<char*(*)(const char*, const char*, size_t)>(strncpy);

यह मान से पारित तर्कों के साथ उत्पन्न नहीं होता है, क्योंकि संकलक उन की प्रतियां बनाता है। वैल्यू const द्वारा पारित एक पैरामीटर को चिह्नित करने का मतलब है कि फ़ंक्शन को इसकी अस्थायी प्रतिलिपि को संशोधित करने की आवश्यकता नहीं है। उदाहरण के लिए, यदि मानक पुस्तकालय ने आंतरिक रूप से char* strncpy( char* const dest, const char* const src, const size_t n ) घोषित किया है, तो यह K & R मुहावरे *dest++ = *src++; का उपयोग करने में सक्षम नहीं होगा *dest++ = *src++; । यह फ़ंक्शन की तर्कों की अस्थायी प्रतियों को संशोधित करता है, जिन्हें हमने const घोषित किया था। चूंकि यह कार्यक्रम के बाकी हिस्सों को प्रभावित नहीं करता है, इसलिए सी का कोई आपत्ति नहीं है यदि आप एक फ़ंक्शन प्रोटोटाइप या पॉइंटर पॉइंटर में एक const क्वालीफ़ायर को जोड़ते या हटाते हैं। आमतौर पर, आप हेडर फ़ाइल में उन्हें सार्वजनिक इंटरफ़ेस का हिस्सा नहीं बनाते हैं, क्योंकि वे कार्यान्वयन विवरण हैं।

¹ यद्यपि मैं सही हस्ताक्षर के साथ एक प्रसिद्ध फ़ंक्शन के उदाहरण के रूप में strncpy() उपयोग करता हूं, यह सामान्य रूप से पदावनत है।


मूल्य से पारित फ़ंक्शन तर्कों के लिए एक विशेष नियम है।

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

वही तुम देख रहे हो।

(व्यक्तिगत रूप से मुझे लगता है कि यह डिजाइन निर्णय एक गलती थी; यह भ्रामक और अनावश्यक है! लेकिन यह वही है जो ध्यान दें। यह उसी मार्ग से आता है जो चुपचाप void foo(T arg[5]); void foo(T* arg); में void foo(T arg[5]); void foo(T* arg); , इसलिए वहाँ बहुत hokey बकवास नहीं है! वहाँ पहले से ही है कि हम से निपटने के लिए है!)

हालांकि, याद रखें, कि यह इस तरह के तर्क के प्रकार में किसी भी const को const नहीं है। int* const में पॉइंटर const , लेकिन int const* (या const int* ) में पॉइंटर नॉन- const लेकिन एक const चीज के लिए होता है। केवल पहला उदाहरण पॉइंटर के const नेस्ट से संबंधित है और इसे छीन लिया जाएगा।

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





function-declaration