मैं समान कॉन्स और गैर-कॉन्स सदस्य कार्यों के बीच कोड डुप्लिकेशन कैसे हटा सकता हूं?


170

चलो कहते हैं कि मैं निम्नलिखित class X जहाँ मैं एक आंतरिक सदस्य के लिए उपयोग वापस जाने के लिए चाहते हैं:

class Z 
{ 
    // details 
}; 

class X 
{ 
    std::vector<Z> vecZ; 

public: 
    Z& Z(size_t index) 
    { 
     // massive amounts of code for validating index 

     Z& ret = vecZ[index]; 

     // even more code for determining that the Z instance 
     // at index is *exactly* the right sort of Z (a process 
     // which involves calculating leap years in which 
     // religious holidays fall on Tuesdays for 
     // the next thousand years or so) 

     return ret; 
    } 
    const Z& Z(size_t index) const 
    { 
     // identical to non-const X::Z(), except printed in 
     // a lighter shade of gray since 
     // we're running low on toner by this point 
    } 
}; 

दो सदस्य कार्यों X::Z() और X::Z() const ब्रेसिज़ के अंदर समान कोड है। यह डुप्लिकेट कोड है और जटिल तर्क के साथ लंबे कार्यों के लिए रखरखाव की समस्याएं पैदा कर सकता है।

क्या इस कोड डुप्लिकेशन से बचने का कोई तरीका है?

  0

इस उदाहरण में मैं कॉन्स्ट केस में एक मूल्य वापस कर दूंगा ताकि आप नीचे रिफैक्टरिंग नहीं कर सकें। int जेड() कॉन्स {रिटर्न जेड; } 23 sep. 082008-09-23 20:50:44

  0

मौलिक प्रकारों के लिए, आप बिल्कुल सही हैं! मेरा पहला उदाहरण बहुत अच्छा नहीं था। आइए मान लें कि इसके बजाय हम इसके बजाय कुछ क्लास इंस्टेंस लौट रहे हैं। (मैंने इसे प्रतिबिंबित करने के लिए प्रश्न अपडेट किया।) 23 sep. 082008-09-23 21:02:19

50

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

class X 
{ 
    std::vector<Z> vecZ; 

public: 
    const Z& Z(size_t index) const 
    { 
     // same really-really-really long access 
     // and checking code as in OP 
     // ... 
     return vecZ[index]; 
    } 

    Z& Z(size_t index) 
    { 
     // One line. One ugly, ugly line - but just one line! 
     return const_cast<Z&>(static_cast<const X&>(*this).Z(index)); 
    } 

#if 0 // A slightly less-ugly version 
    Z& Z(size_t index) 
    { 
     // Two lines -- one cast. This is slightly less ugly but takes an extra line. 
     const X& constMe = *this; 
     return const_cast<Z&>(constMe.Z(index)); 
    } 
#endif 
}; 

नोट: यह महत्वपूर्ण है कि आप नहीं गैर स्थिरांक समारोह में तर्क डाल करते हैं और स्थिरांक-फ़ंक्शन को कॉल गैर स्थिरांक कार्य हो - यह अपरिभाषित व्यवहार हो सकता है। इसका कारण यह है कि निरंतर वर्ग उदाहरण गैर-निरंतर उदाहरण के रूप में डाला जाता है। गैर-कॉन्स्ट सदस्य फ़ंक्शन गलती से कक्षा को संशोधित कर सकता है, जिसे सी ++ मानक राज्यों के परिणामस्वरूप अपरिभाषित व्यवहार होगा।

+2

वाह ... यह भयानक है। आपने अभी कोड की मात्रा में वृद्धि की है, स्पष्टता में कमी आई है, और * दो * stinkin 'const_cast <> s जोड़ा। शायद आपके मन में एक उदाहरण है जहां यह वास्तव में समझ में आता है? 23 sep. 082008-09-23 20:50:53

  0

मैं शोग से सहमत हूं। मैं मूल पसंद करता हूँ! 23 sep. 082008-09-23 20:53:08

  0

हां, वह घृणित जानवर कोड डुप्लिकेशन से भी बदतर है। 23 sep. 082008-09-23 20:55:48

  0

ठीक है, आपने इसे एक साधारण पूर्णांक के अलावा किसी अन्य चीज़ पर काम करने के लिए संशोधित किया है। गैर-डुप्लिकेटिंग कोड से "डुप्लिकेटिंग" कोड * अभी भी * अधिक स्पष्ट है। 23 sep. 082008-09-23 20:59:51

+9

अरे यह डिंग नहीं करते हैं !, यह बदसूरत हो सकता है, लेकिन स्कॉट मेयर्स के अनुसार, यह (लगभग) सही तरीका है। शीर्षक के तहत _ प्रभावशाली सी ++ _, 3 डी एड, आइटम 3 देखें "कॉन्स और गैर-लागत वाले सदस्य कार्यों में डुप्लिकेशन से बचें। 23 sep. 082008-09-23 21:06:15

+8

जबकि मैं समझता हूं कि समाधान बदसूरत हो सकता है, कल्पना करें कि कोड जो निर्धारित करता है कि क्या वापस करना है 50 लाइनें लम्बाई। फिर नकल बहुत अवांछनीय है - खासकर जब आपको कोड को दोबारा कारक बनाना होगा। मुझे अपने करियर में कई बार सामना करना पड़ा है। 23 sep. 082008-09-23 21:06:53

  0

@jwfearn - जिज्ञासा से, क्या गलत है? और स्कॉट मेयर्स और कैसे करते हैं मैं अलग हूं? 23 sep. 082008-09-23 21:07:59

+2

केविन - आपको कम से कम एक संपादन # 6 की आवश्यकता है - बदसूरत सेट में आपके पास "const_cast <int&>" होना चाहिए जो "const_cast <Z&>" होना चाहिए 23 sep. 082008-09-23 21:59:06

+1

समझ गया। धन्यवाद माइक! 23 sep. 082008-09-23 22:03:19

+7

इस और मेयर्स के बीच का अंतर यह है कि मेयर्स में static_cast <const X&> (* यह) है। Const_cast इसे जोड़ने के लिए कॉन्स्ट को हटाने के लिए है। 23 sep. 082008-09-23 22:14:24

  0

से अधिक केएस वनबीन। समझ गया! 23 sep. 082008-09-23 22:24:54

  0

सी ++ मानक के अनुसार, मूल रूप से 'const' के रूप में बनाए गए ऑब्जेक्ट से 'const' qualifier को निकालने के लिए' const_cast' का उपयोग करना अमान्य है। मुझे पता है कि, यह कोड अपरिभाषित व्यवहार का आह्वान करता है। 04 jul. 162016-07-04 11:11:57

+3

@ व्हायोलेट जिराफ हम जानते हैं कि ऑब्जेक्ट मूल रूप से कॉन्स नहीं बनाया गया था, क्योंकि यह एक गैर-कॉन्स ऑब्जेक्ट का गैर-कॉन्स्ट सदस्य है, जिसे हम जानते हैं क्योंकि हम उस ऑब्जेक्ट की गैर-कॉन्स्ट विधि में हैं। संकलक इस अनुमान को नहीं बनाता है, यह एक रूढ़िवादी नियम का पालन करता है। ऐसा क्यों लगता है कि इस तरह की स्थिति के लिए नहीं, const_cast मौजूद है? 15 mar. 172017-03-15 12:14:17


-1

This DDJ article टेम्पलेट विशेषज्ञता का उपयोग करके एक तरीका दिखाता है जिसके लिए आपको const_cast का उपयोग करने की आवश्यकता नहीं है। इस तरह के एक साधारण समारोह के लिए हालांकि वास्तव में इसकी आवश्यकता नहीं है।

बूस्ट :: any_cast (एक बिंदु पर, यह अब और नहीं है) नॉन-कॉन्स संस्करण को दोहराने से बचने के लिए कॉन्स्ट संस्करण से एक const_cast का उपयोग करता है। आप गैर-कॉन्स संस्करण पर कॉन्स अर्थशास्त्र लागू नहीं कर सकते हैं, हालांकि आपको बहुत से सावधान रहना होगा।

अंत में कुछ कोड डुप्लिकेशन ठीक है जब तक कि दो स्निपेट सीधे एक दूसरे के शीर्ष पर हों।

  0

डीडीजे आलेख इटरेटर को संदर्भित करता है - जो प्रश्न के लिए प्रासंगिक नहीं है। कॉन्स्ट-इटरेटर्स निरंतर डेटा नहीं होते हैं - वे इटरेटर हैं जो निरंतर डेटा को इंगित करते हैं। 23 sep. 082008-09-23 21:05:11


0

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

+2

यह ज्यादातर समय सच हो सकता है। लेकिन अपवाद हैं। 23 sep. 082008-09-23 21:14:11

+1

वेटर्स वैसे भी, एक कॉन्स सेटर बहुत समझ में नहीं आता है;) 23 sep. 082008-09-23 21:46:20

  0

मेरा मतलब था कि गैर-कॉन्स गेटर प्रभावी ढंग से एक सेटटर है। :) 23 sep. 082008-09-23 22:00:14


139

विस्तृत स्पष्टीकरण के लिए, कृपया "const में डुप्लिकेशन से बचें और गैर-const सदस्य फ़ंक्शन" पर जाएं। 9780321334879.

alt text

यहाँ मेयर्स 'समाधान (सरलीकृत):

23, आइटम 3 में प्रभावी सी ++, स्कॉट Meyers, ISBN-13 से 3 डी एड में "const का उपयोग करें जब भी संभव हो,"
struct C { 
    const char & get() const { 
    return c; 
    } 
    char & get() { 
    return const_cast<char &>(static_cast<const C &>(*this).get()); 
    } 
    char c; 
}; 

दो कास्ट और फ़ंक्शन कॉल बदसूरत हो सकती है लेकिन यह सही है। मेयर्स का एक संपूर्ण स्पष्टीकरण क्यों है।

+27

स्कॉट मेयर्स का पालन करने के लिए किसी को भी कभी नहीं निकाल दिया गया :-) 23 sep. 082008-09-23 22:15:10

  0

const_cast का लगभग कभी भी उपयोग नहीं किया जाना चाहिए। यह कॉन्स-नेस तोड़ता है। यह embeeded सिस्टम पर especialy सच है जहां आपके पास रोम है। सामान्य रूप से const_cast का उपयोग करना एक बुरा विचार है। 24 sep. 082008-09-24 00:03:13

+4

ओच ... अगर आप const == ROM पर गिन रहे हैं तो आप पहले से ही परेशानी में हो सकते हैं।कंपाइलर्स इतने मजबूत दावे को साबित करने के लिए पर्याप्त नहीं हैं। 24 sep. 082008-09-24 00:30:04

+7

witkamp सही है कि आम तौर पर const_cast का उपयोग करना बुरा होता है। यह एक विशिष्ट मामला है जहां यह नहीं है, जैसा कि मेयर्स बताते हैं। @ एडम: रॉम => कॉन्स ठीक है। कॉन्स == रॉम स्पष्ट रूप से बकवास है क्योंकि कोई भी गैर-कॉन्स को कॉन्स्टिल्ड करने के लिए नहीं बना सकता है: यह केवल कुछ संशोधित न करने का चयन करने के बराबर है। 24 sep. 082008-09-24 02:35:08

+28

सामान्य रूप से मैं स्थिरांक को कॉस्ट जोड़ने के बजाय const_cast का उपयोग करने का सुझाव दूंगा क्योंकि यह आपको गलती से प्रकार को बदलने से रोकता है। 23 nov. 082008-11-23 07:49:39

+1

@ विटककैम्प आप बिंदु खो रहे हैं, दो प्रकार की स्थिरता है: भौतिक और तार्किक। 14 nov. 122012-11-14 21:45:52

+2

क्या होगा यदि आपका 'कॉन्स char & get() const' कुछ वास्तविक घोषित कॉन्स है? फिर 'get' का आपका गैर-कॉन्स संस्करण प्रभावी रूप से कुछ ऐसा करने वाला होगा जो लेखन के लिए कभी भी सुलभ नहीं था। 28 mar. 132013-03-28 06:54:57

+6

@ हैलोगुडबी: मुझे लगता है कि मेयर्स कक्षा इंटरफ़ेस के डिजाइनर से खुफिया जानकारी का * मॉडिकम * मानता है। यदि 'get() const' कुछ ऐसा करता है जिसे कॉन्स्ट ऑब्जेक्ट के रूप में परिभाषित किया गया था, तो' get() 'का गैर-कॉन्स्ट संस्करण नहीं होना चाहिए। असल में इस पर मेरी सोच समय के साथ बदल गई है: टेम्पलेट समाधान डुप्लिकेशंस से बचने का एकमात्र तरीका है * और * कंपाइलर-चेक कॉन्स्ट-कॉन्फ़िगरेशन प्राप्त करें, इसलिए व्यक्तिगत रूप से मैं डुप्लिकेटिंग कोड से बचने के लिए 'const_cast' का उपयोग नहीं करूंगा, I डुप्लिकेट कोड को फ़ंक्शन टेम्पलेट में डालने के बीच या अन्यथा इसे डुप्लिकेट करने के बीच चुनें। 05 apr. 132013-04-05 09:37:17

+6

निम्नलिखित दो टेम्पलेट्स इस समाधान की पठनीयता के साथ बहुत मदद करते हैं: 'टेम्पलेट <typename T> कॉन्स टी और निरंतर (टी एंड _) {वापसी const_cast <const t &>(_);} 'और' टेम्पलेट <typename T> टी और चर (कॉन्स्ट टी एंड _) {वापसी const_cast <टी &>(_); } '। तो फिर तुम कर सकते हैं: 'लौट चर (निरंतर (* इस) .Get());' 21 jul. 142014-07-21 12:46:31

  0

ज़रूर, बिल्कुल बराबर है, लेकिन मैं इस प्रकार से थोड़ा अधिक सौंदर्य पर विचार करें: 'const_cast लौट <char &> (const_cast <const C *> (यह) -> मिल ()); ' 26 jun. 172017-06-26 14:43:16

  0

मैं विश्वास नहीं कर सकता स्कॉट किया इस तरह कोड :) 22 oct. 172017-10-22 09:23:15


16

एक मेयर्स की तुलना में अधिक वर्बोज़ सा है, लेकिन मैं यह कर सकता है:

class X { 

    private: 

    // This method MUST NOT be called except from boilerplate accessors. 
    Z &_getZ(size_t index) const { 
     return something; 
    } 

    // boilerplate accessors 
    public: 
    Z &getZ(size_t index)    { return _getZ(index); } 
    const Z &getZ(size_t index) const { return _getZ(index); } 
}; 

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

ध्यान दें कि टिप्पणियां पैटर्न का हिस्सा हैं - _getZ का इंटरफ़ेस निर्दिष्ट करता है कि इसे कॉल करने के लिए कभी भी वैध नहीं है (स्पष्ट रूप से एक्सेसर्स से अलग): ऐसा करने के लिए कोई कल्पनीय लाभ नहीं है, क्योंकि यह टाइप करने के लिए 1 और वर्ण है और परिणामस्वरूप छोटे या तेज़ कोड नहीं होंगे। विधि को कॉल करना एक कॉन्स्ट_कास्ट वाले एक्सेसर्स में से किसी एक को कॉल करने के बराबर है, और आप इसे भी नहीं करना चाहेंगे। यदि आप त्रुटियों को स्पष्ट करने के बारे में चिंतित हैं (और यह एक उचित लक्ष्य है), तो _getZ के बजाय इसे const_cast_getZ पर कॉल करें।

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

[संपादित करें: केविन ने सही ढंग से इंगित किया है कि _getZ एक और विधि (जेनरजेड कहें) को कॉल करना चाह सकता है जो कि GetZ है। इस मामले में, _getZ एक कॉन्स जेड & देखेंगे और इसे वापस करने से पहले इसे const_cast करना होगा। यह अभी भी सुरक्षित है, क्योंकि बॉयलरप्लेट एक्सेसर सबकुछ दिखाता है, लेकिन यह स्पष्ट रूप से स्पष्ट नहीं है कि यह सुरक्षित है। इसके अलावा, यदि आप ऐसा करते हैं और उसके बाद जेनरजेड को हमेशा कॉन्स लौटने के लिए बदलते हैं, तो आपको हमेशा गेज को वापस करने के लिए getZ को बदलने की आवश्यकता होती है, लेकिन कंपाइलर आपको यह नहीं बताएगा कि आप करते हैं।

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

+1

यह अभी भी समस्या है जो बात तुम वापस एक्स उस मामले में की एक निरंतर उदाहरण के लिए निरंतर हो सकता है, तो आप अभी भी _getZ में एक const_cast की आवश्यकता होती है (...)। यदि बाद के डेवलपर्स द्वारा दुरुपयोग किया जाता है, तो यह अभी भी यूबी का नेतृत्व कर सकता है। यदि वापस लौटाई जाने वाली चीज़ 'उत्परिवर्तनीय' है, तो यह एक अच्छा समाधान है। 24 sep. 082008-09-24 00:48:24

  0

जब मैंने कहा कि '_getZ (...)' का गलत इस्तेमाल किया जा सकता है, मेरा मतलब था कि यदि भविष्य के डेवलपर को यह समझ में नहीं आया कि इसका उपयोग केवल सार्वजनिक कार्यों द्वारा किया जाना था और इसे सीधे कहा जाता था, लेकिन मूल्य संशोधित किया गया था एक्स के निरंतर उदाहरण में, जिससे यूबी हो सकता है। यदि संभवतः दस्तावेज नहीं किया गया तो यह संभव है। 24 sep. 082008-09-24 00:50:53

  0

किसी भी निजी फ़ंक्शन (बिल्ली, सार्वजनिक भी) को बाद के डेवलपर्स द्वारा गलत इस्तेमाल किया जा सकता है, यदि वे ब्लैक कैपिटल निर्देशों को अपने वैध उपयोग, हेडर फ़ाइल में और डॉक्सिजन आदि में अनदेखा करना चुनते हैं। मैं इसे रोक नहीं सकता , और मैं इसे मेरी समस्या पर विचार नहीं करता क्योंकि निर्देशों को समझना आसान है। 24 sep. 082008-09-24 01:44:46

+8

-1: यह कई स्थितियों में काम नहीं करता है। क्या होगा यदि '_getZ()' फ़ंक्शन में 'कुछ' एक उदाहरण चर है? कंपाइलर (या कम से कम कुछ कंपाइलर्स) शिकायत करेंगे कि चूंकि '_getZ()' const है, किसी भी इंस्टेंस वैरिएबल के भीतर संदर्भित भी है। तो 'कुछ' तब होगा (यह प्रकार 'कॉन्स जेड' 'होगा और इसे 'Z &' में परिवर्तित नहीं किया जा सका। मेरे (स्वीकार्य रूप से कुछ हद तक सीमित) अनुभव में, अधिकांश समय 'कुछ' इस तरह के मामलों में एक आवृत्ति चर है। 04 aug. 112011-08-04 21:10:14

+2

@ ग्रेविटीब्रिंगर: फिर "कुछ" को 'const_cast' शामिल करने की आवश्यकता है। इसका उद्देश्य कॉन्स ऑब्जेक्ट से गैर-कॉन्स रिटर्न प्राप्त करने के लिए आवश्यक कोड के लिए प्लेस धारक होना था, जो कि प्लेसहोल्डर के रूप में नहीं था, जो डुप्लीकेट गेटर में * होगा। तो "कुछ" सिर्फ एक आवृत्ति चर नहीं है। 05 aug. 112011-08-05 08:54:13

+2

मैं देखता हूं। यह वास्तव में तकनीक की उपयोगिता को कम करता है, हालांकि। मैं डाउनवोट को हटा दूंगा, लेकिन एसओ मुझे नहीं जाने देगा। 06 aug. 112011-08-06 22:43:06

  0

यह प्रभावी रूप से मेयर्स के समान ही चीज़ प्राप्त करता है, केवल तभी आपका 'const_cast'' कुछ 'में छिपा हुआ है। यदि 'कुछ' को 'const_caste'd' नहीं होना चाहिए, तो ऐसा इसलिए होगा क्योंकि उस ऑब्जेक्ट को संशोधित करने के लिए इसका उपयोग नहीं किया जा सकता था जिस पर विधि को बुलाया गया था और फिर आपको किसी गैर-कॉन्स्ट संस्करण संस्करण की आवश्यकता नहीं होगी प्रक्रिया। 04 apr. 132013-04-04 19:52:52

+1

@ हैलोगुडबी: यह सही नहीं है। यह सच नहीं है कि अलग-अलग कॉन्स और गैर-कॉन्स संस्करणों की आवश्यकता केवल तभी होती है जब 'const_cast' की आवश्यकता हो।उदाहरण के लिए, 'कुछ' 'some_pointer_data_member' तक उबाल सकता है (और मान लीजिए कि आप कॉन्स्ट केस में एक कॉन्स संदर्भ वापस करना चाहते हैं), इस मामले में मेयर्स का कोड एक कलाकार पेश करता है लेकिन आपको वास्तव में एक की आवश्यकता नहीं होती है। उनका कोड एक पूरी तरह से सामान्य पैटर्न प्रदान करता है, लेकिन आपके द्वारा लिखे जाने वाले अधिकांश वास्तविक कार्य अधिक विशिष्ट हैं :-) 05 apr. 132013-04-05 09:26:18

  0

ठीक है, मुझे लगता है कि आप उस मामले में अपने स्वयं के क्लास इंस्टेंस को संशोधित कर सकते हैं, उदाहरण के लिए, कम से कम अगर सूचक सदस्य उदाहरण में या उदाहरण के लिए किसी सदस्य को इंगित करता है, क्योंकि संकलक को पता नहीं है कि सूचक क्या संबोधित कर रहा है। उसने अब तक मुझे नहीं मारा। 05 apr. 132013-04-05 13:24:11

  0

यह एक बहुत ही वैचारिक उत्तर है, जब यह लौटा संदर्भ कक्षा का सूचक होता है तो यह बहुत समझ में आता है। 19 oct. 152015-10-19 18:45:44

  0

@ हैलोगुडबी, छुपा 'const_cast' कहां है? अगर लौटाई गई चीज नहीं है, तो कोड पूरी तरह से मान्य है। इस उत्तर का मुद्दा यह है कि यह 'const_cast' अंतर्निहित या स्पष्ट का उपयोग नहीं करता है। यह केवल एक सामान्य सम्मेलन तोड़कर प्रभाव प्राप्त करता है, लेकिन यह अपने निजी कार्यान्वयन में करता है। 19 oct. 152015-10-19 18:50:49


3

तर्क को एक निजी विधि में कैसे ले जाना है, और केवल गेटर्स के अंदर "संदर्भ और वापसी" सामान कर रहे हैं? असल में, मैं स्थिर गठबंधन समारोह के अंदर स्थैतिक और कॉन्स के बारे में काफी उलझन में रहूंगा, और मैं बेहद दुर्लभ परिस्थितियों को छोड़कर बदसूरत मानता हूं!

  0

अपरिभाषित व्यवहार से बचने के लिए आपको अभी भी एक const_cast की आवश्यकता है। मार्टिन यॉर्क और मेरी टिप्पणी के जवाब देखें। 23 sep. 082008-09-23 22:28:05

+1

केविन, मार्टिन यॉर्क 03 mar. 112011-03-03 18:46:57


6

आप इसे टेम्पलेट्स के साथ भी हल कर सकते हैं। यह समाधान थोड़ा बदसूरत है (लेकिन कुरूपता .cpp फ़ाइल में छिपी हुई है) लेकिन यह स्थिरता की कंपाइलर जांच प्रदान करता है, और कोई कोड डुप्लिकेशन नहीं देता है।

ज फ़ाइल:

#include <vector> 

class Z 
{ 
    // details 
}; 

class X 
{ 
    std::vector<Z> vecZ; 

public: 
    const std::vector<Z>& GetVector() const { return vecZ; } 
    std::vector<Z>& GetVector() { return vecZ; } 

    Z& GetZ(size_t index); 
    const Z& GetZ(size_t index) const; 
}; 

.cpp फ़ाइल:

#include "constnonconst.h" 

template< class ParentPtr, class Child > 
Child& GetZImpl(ParentPtr parent, size_t index) 
{ 
    // ... massive amounts of code ... 

    // Note you may only use methods of X here that are 
    // available in both const and non-const varieties. 

    Child& ret = parent->GetVector()[index]; 

    // ... even more code ... 

    return ret; 
} 

Z& X::GetZ(size_t index) 
{ 
    return GetZImpl< X*, Z >(this, index); 
} 

const Z& X::GetZ(size_t index) const 
{ 
    return GetZImpl< const X*, const Z >(this, index); 
} 

मुख्य नुकसान मैं देख सकता हूँ कि सभी विधि के जटिल कार्यान्वयन एक वैश्विक समारोह में है क्योंकि, आप या तो है उपरोक्त GetVector() जैसे सार्वजनिक विधियों का उपयोग करके एक्स के सदस्यों को पकड़ने की आवश्यकता है (जिनमें से हमेशा एक कॉन्स और गैर-कॉन्स संस्करण होना आवश्यक है) या आप इस फ़ंक्शन को एक दोस्त बना सकते हैं। लेकिन मुझे दोस्तों को पसंद नहीं है।

[संपादित करें: अनावश्यक हटाया परीक्षण के दौरान जोड़ा cstdio के शामिल हैं।]

+3

का क्या जवाब है आप हमेशा जटिल सदस्यों को निजी सदस्यों तक पहुंच प्राप्त करने के लिए स्थिर सदस्य बना सकते हैं। फ़ंक्शन को केवल वर्ग शीर्षलेख फ़ाइल में घोषित करने की आवश्यकता है, परिभाषा कक्षा कार्यान्वयन फ़ाइल में रह सकती है। यह सब के बाद, कक्षा कार्यान्वयन का हिस्सा है। 13 jan. 092009-01-13 17:49:34

  0

आह हाँ अच्छा विचार! मुझे हेडर में दिखाई देने वाली टेम्पलेट सामग्री पसंद नहीं है, लेकिन यदि यहां से यह संभावित रूप से लागू करने के लिए काफी सरल बनाता है तो यह शायद इसके लायक है। इस समाधान के लिए 14 jan. 092009-01-14 09:15:11

  0

+ 1 जो किसी भी कोड को डुप्लिकेट नहीं करता है, न ही किसी भी बदसूरत 'const_cast' का उपयोग करता है (जो गलती से ऐसा कुछ करने के लिए उपयोग किया जा सकता है जो _actually_ को ऐसा कुछ नहीं माना जाता है)। 04 apr. 132013-04-04 20:01:06


27

मुझे लगता है कि स्कॉट Meyers 'समाधान सी ++ 11 एक tempate सहायक फ़ंक्शन का उपयोग करके में सुधार किया जा सकता। इससे इरादा अधिक स्पष्ट हो जाता है और कई अन्य गेटर्स के लिए इसका पुन: उपयोग किया जा सकता है।

template <typename T> 
struct NonConst {typedef T type;}; 
template <typename T> 
struct NonConst<T const> {typedef T type;}; //by value 
template <typename T> 
struct NonConst<T const&> {typedef T& type;}; //by reference 
template <typename T> 
struct NonConst<T const*> {typedef T* type;}; //by pointer 
template <typename T> 
struct NonConst<T const&&> {typedef T&& type;}; //by rvalue-reference 

template<typename TConstReturn, class TObj, typename... TArgs> 
typename NonConst<TConstReturn>::type likeConstVersion(
    TObj const* obj, 
    TConstReturn (TObj::* memFun)(TArgs...) const, 
    TArgs&&... args) { 
     return const_cast<typename NonConst<TConstReturn>::type>(
     (obj->*memFun)(std::forward<TArgs>(args)...)); 
} 

इस सहायक कार्य को निम्न तरीके से उपयोग किया जा सकता है।

struct T { 
    int arr[100]; 

    int const& getElement(size_t i) const{ 
     return arr[i]; 
    } 

    int& getElement(size_t i) { 
     return likeConstVersion(this, &T::getElement, i); 
    } 
}; 

पहला तर्क हमेशा यह सूचक है। दूसरा कॉल करने के लिए सदस्य समारोह के सूचक है। इसके बाद अतिरिक्त तर्कों की मनमानी राशि पारित की जा सकती है ताकि उन्हें कार्य में अग्रेषित किया जा सके। विविधता वाले टेम्पलेट्स के कारण इसे C++ 11 की आवश्यकता है।

+1

यह एक शर्म की बात है कि हमारे पास 'std :: remove_const' के साथ जाने के लिए' std :: remove_bottom_const' नहीं है। 04 feb. 162016-02-04 03:58:38

  0

@ टीबीबीएल कैसे? http://www.cplusplus.com/reference/type_traits/remove_const/ 04 mar. 162016-03-04 03:51:38

  0

मुझे यह समाधान पसंद नहीं है क्योंकि यह अभी भी 'const_cast' एम्बेड करता है। आप 'getElement' को टेम्पलेट स्वयं ही बना सकते हैं, और' mpl :: सशर्त 'प्रकारों के अंदर टाइप की विशेषता का उपयोग कर सकते हैं, जैसे कि' इटरेटर 'या' कन्फिटरेटर 'की आवश्यकता हो। वास्तविक समस्या यह है कि किसी विधि के एक कॉन्स्ट संस्करण को कैसे उत्पन्न किया जाए जब हस्ताक्षर के इस भाग को टेम्पलेट नहीं किया जा सकता है? 04 mar. 162016-03-04 03:54:56

+2

@ v.oddou: 'std :: remove_const <int const&>' int const & '(शीर्ष-स्तरीय' const' योग्यता को हटाएं) है, इसलिए इस उत्तर में 'NonConst <T> 'की जिम्नास्टिक। पुष्टिकरण 'std :: remove_bottom_const' नीचे-स्तर 'const' योग्यता को हटा सकता है, और ठीक से' NonConst <T> 'क्या करता है:' std :: remove_bottom_const <int const&> :: टाइप' => 'int &'। 04 mar. 162016-03-04 08:22:13

  0

@ v.oddou: मुझे लगता है कि आपने जो सही कारण बताया है, उसके लिए आपको लगभग दो अलग-अलग घोषणाएं नहीं मिल सकती हैं। 04 mar. 162016-03-04 09:51:50

  0

और किसी को उत्पादन समाधान में इस समाधान का उपयोग नहीं करना चाहिए। यह भी "मुश्किल" है। या तो अपने कोड को डुप्लिकेट करें यदि यह काफी आसान है, या स्कॉट मेयर के समाधान का उपयोग करें, क्योंकि यह एक बदसूरत लेकिन प्रसिद्ध मुहावरे है। 04 mar. 162016-03-04 09:59:01

+3

यह समाधान अच्छी तरह से काम नहीं करता है, अगर 'getElement' अधिभारित है। फिर फ़ंक्शन पॉइंटर को टेम्पलेट पैरामीटर स्पष्ट रूप से दिए बिना हल नहीं किया जा सकता है। क्यूं कर? 20 jun. 162016-06-20 14:09:04

  0

मैं जॉन से सहमत हूं। समर्थन करने के लिए, यहां एक परीक्षण है: http://ideone.com/fwqmsB। यह वर्तमान में संकलित करता है, लेकिन अगर असम्बद्धता/**/(ओवरलोड फ़ंक्शन) है, तो यह अब संकलित नहीं होगा। यह अच्छा होगा अगर इस समाधान को थोड़ा और बेहतर किया जाए। 22 mar. 172017-03-22 05:51:19

  0

@ जॉन यह सदस्य फ़ंक्शन पॉइंटर्स के लिए सी ++ ओवरलोड रिज़ॉल्यूशन के साथ एक समस्या है (यह भी देखें http://stackoverflow.com/questions/2942426/how-do-i-specify-a-pointer-to-an-overloaded- कार्यक्षमता) । वर्तमान में यह केवल कॉलर पक्ष पर हल किया जा सकता है। हालांकि आपको सभी टेम्पलेट पैरामीटर निर्दिष्ट करने की आवश्यकता नहीं है। केवल पहला 'TConstReturn' भी काम करता है। जैसे: 'वापसी की तरह कॉन्स्टवर्सन <int const&> (यह, और टी :: getElement, i); ' 11 apr. 172017-04-11 10:42:13

  0

आपको सी ++ 11 सही अग्रेषण का उपयोग करने के लिए आपको जवाब देने की आवश्यकता है: ' जैसे कॉनस्टवर्जन (TOBj const * obj, TConstReturn (TObj :: * memFun) (TArgs ...) const, TArgs && ... args) { वापसी const_cast <typename nonConst <TConstReturn> :: type> ((obj -> * memFun) (std :: forward <TArgs> (args) ...)); } ' पूर्ण: https://gist.github.com/BlueSolei/bca26a8590265492e2f2760d3cefcf83 18 may. 172017-05-18 17:34:44


13

अच्छा प्रश्न और अच्छा जवाब।

class X { 

private: 

    std::vector<Z> v; 

    template<typename InstanceType> 
    static auto get(InstanceType& instance, std::size_t i) -> decltype(instance.get(i)) { 
     // massive amounts of code for validating index 
     // the instance variable has to be used to access class members 
     return instance.v[i]; 
    } 

public: 

    const Z& get(std::size_t i) const { 
     return get(*this, i); 
    } 

    Z& get(std::size_t i) { 
     return get(*this, i); 
    } 

}; 

हालांकि, यह एक स्थिर सदस्य और इसके अंदर instance चर का उपयोग कर की जरूरत की आवश्यकता होती है की कुरूपता है: मैं एक और समाधान, कोई डाले का उपयोग करता है।

मैंने इस समाधान के सभी संभावित (नकारात्मक) प्रभावों पर विचार नहीं किया। अगर कोई है तो कृपया मुझे बताएं।

+3

ठीक है, सरल तथ्य के साथ जाएं कि आपने अधिक बॉयलरप्लेट जोड़ा है। यदि कुछ भी हो, तो इसका उपयोग इस उदाहरण के रूप में किया जाना चाहिए कि भाषा को रिटर्न टाइप 'ऑटो get (std :: size_t i) -> ऑटो (कॉन्स), ऑटो (&&) के साथ फ़ंक्शन क्वालीफायर को संशोधित करने के तरीके की आवश्यकता क्यों है। '। क्यूं कर '&&'? आह, तो मैं कह सकता हूं: 'ऑटो foo() -> ऑटो (कॉन्स), ऑटो (&&) = हटाएं; ' 27 apr. 152015-04-27 23:40:59

  0

gd1: ठीक है जो मुझे दिमाग में था। @kfsone और वास्तव में मैंने भी क्या निष्कर्ष निकाला। 04 mar. 162016-03-04 03:57:56

+1

@kfsone वाक्यविन्यास 'इस' कीवर्ड को शामिल करना चाहिए। मैं सुझाव देता हूं कि 'टेम्पलेट <टाइपनाम टी> ऑटो मायफंक्शन (टी यह, टी तर्क) -> decltype (पहचान)' यह कीवर्ड निहित ऑब्जेक्ट इंस्टेंस तर्क के रूप में पहचाना जाएगा और संकलक को यह पहचानने दें कि मेरा कार्य एक सदस्य है या 'टी' । कॉल टी पर ऑटो टी को स्वचालित रूप से घटाया जाएगा, जो हमेशा कक्षा का प्रकार होगा, लेकिन मुफ्त सीवी योग्यता के साथ। 04 mar. 162016-03-04 03:58:55

  0

यह समाधान http://stackoverflow.com/a/38751554/259543 के बराबर है। यही है, सार्वभौमिक संदर्भों का उपयोग करके '* इस 'क्वालीफायरों पर इंटरपोल करने के लिए एक स्थिर सदस्य कार्य। 10 aug. 162016-08-10 22:24:10

  0

उस समाधान का लाभ भी है ('const_cast' एक बनाम)' iterator' और 'const_iterator' को वापस करने की अनुमति देने के लिए। 08 aug. 172017-08-08 11:47:30

  0

यदि कार्यान्वयन सीपीपी फ़ाइल में स्थानांतरित किया गया है (और डुप्लिकेट करने की विधि को छोटा नहीं होना चाहिए, तो यह संभवतः मामला होगा), 'स्थिर 'कक्षा के दायरे के बजाय फ़ाइल स्कोप पर किया जा सकता है। :-) 08 aug. 172017-08-08 11:51:45

  0

@ जारोड 42 मिमी निजी सदस्य? 08 aug. 172017-08-08 11:53:01

  0

@ gd1: यदि आप उचित हैं, तो आप उन्हें * * इस' के अतिरिक्त/प्रतिस्थापन में भी पास कर सकते हैं। 08 aug. 172017-08-08 11:58:20

  0

@ जारोड 42 बिट थोड़ा बदसूरत 08 aug. 172017-08-08 12:00:16

  0

'वापसी get_impl (v, i);' बनाम 'वापसी get_impl (* यह, i);' आईएमओ नहीं है, लेकिन 'get_impl (* यह, v, सदस्य 1, सदस्य 2, i) वापस लौटें; 'होगा, और उस स्थिति में, टेम्पलेट स्थिर सदस्य अभी भी उपलब्ध है (और सीपीपी फ़ाइल (संबंधित गेटर्स के साथ) में लागू किया जा सकता है जैसा कि केवल 2 स्थानों पर उपयोग किया जाता है)। 08 aug. 172017-08-08 12:07:02


0

समाधान jwfearn को जोड़ने के लिए, और केविन प्रदान की है, यहां इसी समाधान है जब फ़ंक्शन shared_ptr:

struct C { 
    shared_ptr<const char> get() const { 
    return c; 
    } 
    shared_ptr<char> get() { 
    return const_pointer_cast<char>(static_cast<const C &>(*this).get()); 
    } 
    shared_ptr<char> c; 
}; 

1

मैं एक दोस्त है जो हक const_cast के उपयोग को सही ठहराया के लिए ऐसा किया ... के बारे में जानते हुए भी नहीं यह मैं शायद कुछ इस तरह (वास्तव में सुरुचिपूर्ण नहीं) किया होता:

#include <iostream> 

class MyClass 
{ 

public: 

    int getI() 
    { 
     std::cout << "non-const getter" << std::endl; 
     return privateGetI<MyClass, int>(*this); 
    } 

    const int getI() const 
    { 
     std::cout << "const getter" << std::endl; 
     return privateGetI<const MyClass, const int>(*this); 
    } 

private: 

    template <class C, typename T> 
    static T privateGetI(C c) 
    { 
     //do my stuff 
     return c._i; 
    } 

    int _i; 
}; 

int main() 
{ 
    const MyClass myConstClass = MyClass(); 
    myConstClass.getI(); 

    MyClass myNonConstClass; 
    myNonConstClass.getI(); 

    return 0; 
} 

0

मैं एक निजी सहायक स्थिर समारोह टेम्पलेट सुझाव देंगे, इस तरह:

class X 
{ 
    std::vector<Z> vecZ; 

    // ReturnType is explicitly 'Z&' or 'const Z&' 
    // ThisType is deduced to be 'X' or 'const X' 
    template <typename ReturnType, typename ThisType> 
    static ReturnType Z_impl(ThisType& self, size_t index) 
    { 
     // massive amounts of code for validating index 
     ReturnType ret = self.vecZ[index]; 
     // even more code for determining, blah, blah... 
     return ret; 
    } 

public: 
    Z& Z(size_t index) 
    { 
     return Z_impl<Z&>(*this, index); 
    } 
    const Z& Z(size_t index) const 
    { 
     return Z_impl<const Z&>(*this, index); 
    } 
}; 
  0

डर्न! जीडी 1 के समान जवाब, मैं उसका उत्तर उखाड़ दूंगा क्योंकि मुझे लगता है कि यह 07 oct. 152015-10-07 14:26:09


0

मैं के लिए क्या देख रहा था वह नहीं मिला, तो मैं अपने ही के एक जोड़े लुढ़का ...

यह एक एक छोटे से अधिक शब्दों वाले है, लेकिन एक ही नाम के कई अतिभारित तरीकों से निपटने का लाभ दिया है (और वापसी प्रकार) सभी को एक बार: आप नाम में केवल एक const विधि, लेकिन अभी भी तरीकों की नकल करने के बहुत सारे है, तो

struct C { 
    int x[10]; 

    int const* getp() const { return x; } 
    int const* getp(int i) const { return &x[i]; } 
    int const* getp(int* p) const { return &x[*p]; } 

    int const& getr() const { return x[0]; } 
    int const& getr(int i) const { return x[i]; } 
    int const& getr(int* p) const { return x[*p]; } 

    template<typename... Ts> 
    auto* getp(Ts... args) { 
    auto const* p = this; 
    return const_cast<int*>(p->getp(args...)); 
    } 

    template<typename... Ts> 
    auto& getr(Ts... args) { 
    auto const* p = this; 
    return const_cast<int&>(p->getr(args...)); 
    } 
}; 

, तो आप इस पसंद कर सकते हैं:

template<typename T, typename... Ts> 
    auto* pwrap(T const* (C::*f)(Ts...) const, Ts... args) { 
    return const_cast<T*>((this->*f)(args...)); 
    } 

    int* getp_i(int i) { return pwrap(&C::getp_i, i); } 
    int* getp_p(int* p) { return pwrap(&C::getp_p, p); } 

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

template<typename... Ts> 
    auto* getp(Ts... args) { return pwrap<int, Ts...>(&C::getp, args...); } 

लेकिन const विधि के संदर्भ में तर्क टेम्पलेट के लिए जाहिरा तौर पर दर-मूल्य तर्कों के खिलाफ मैच के लिए असफल और यह टूट जाता है। यकीन नहीं क्यों।Here's why


2

क्या यह प्रीप्रोसेसर का उपयोग करने के लिए धोखा दे रहा है?

struct A { 

    #define GETTER_CORE_CODE  \ 
    /* line 1 of getter code */ \ 
    /* line 2 of getter code */ \ 
    /* .....etc............. */ \ 
    /* line n of getter code */  

    //^NOTE: line continuation char '\' on all lines but the last 

    B& get() { 
     GETTER_CORE_CODE 
    } 

    const B& get() const { 
     GETTER_CORE_CODE 
    } 

    #undef GETTER_CORE_CODE 

}; 

यह टेम्पलेट्स या डाले के रूप में के रूप में पसंद नहीं है, लेकिन यह अपने इरादे पड़ता है ("इन दोनों कार्यों समान हो रहे हैं") बहुत स्पष्ट।

  0

करने का सही तरीका है लेकिन फिर आपको बैकस्लाश (मल्टीलाइन मैक्रोज़ के लिए सामान्य रूप से) के साथ सावधान रहना होगा और इसके अतिरिक्त आप अधिकांश में सिंटैक्स हाइलाइटिंग खो देंगे (अगर सभी नहीं) संपादक। 23 jan. 172017-01-23 14:01:40


3

सी ++ 17 इस प्रश्न के लिए सर्वश्रेष्ठ उत्तर को अपडेट किया गया:

T const & f() const { 
    return something_complicated(); 
} 
T & f() { 
    return const_cast<T &>(std::as_const(*this).f()); 
} 

यह फायदे है कि यह:

  • स्पष्ट है क्या
  • पर जा रहा है कम से कम कोड भूमि के ऊपर है - यह एक पंक्ति में फिट बैठता है
  • गलत होना मुश्किल है (केवल volatile को दुर्घटना से दूर कर सकता है, लेकिन volatile एक दुर्लभ क्वालीफ है ier)
  0

['std :: add_const'] (http: //en.cppreference] के बजाय [' std :: as_const'] (http://en.cppreference.com/w/cpp/utility/as_const) होना चाहिए। com/डब्ल्यू/सीपीपी/प्रकार/add_cv)। 18 jan. 182018-01-18 09:13:00

  0

@ s3rvac: धन्यवाद, तय 19 jan. 182018-01-19 15:26:56