2008-09-23 10 views
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 जेड() कॉन्स {रिटर्न जेड; } –

+0

मौलिक प्रकारों के लिए, आप बिल्कुल सही हैं! मेरा पहला उदाहरण बहुत अच्छा नहीं था। आइए मान लें कि इसके बजाय हम इसके बजाय कुछ क्लास इंस्टेंस लौट रहे हैं। (मैंने इसे प्रतिबिंबित करने के लिए प्रश्न अपडेट किया।) – Kevin

उत्तर

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 जोड़ा। शायद आपके मन में एक उदाहरण है जहां यह वास्तव में समझ में आता है? – Shog9

+0

मैं शोग से सहमत हूं। मैं मूल पसंद करता हूँ! – Rob

+0

हां, वह घृणित जानवर कोड डुप्लिकेशन से भी बदतर है। –

-1

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

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

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

+0

डीडीजे आलेख इटरेटर को संदर्भित करता है - जो प्रश्न के लिए प्रासंगिक नहीं है। कॉन्स्ट-इटरेटर्स निरंतर डेटा नहीं होते हैं - वे इटरेटर हैं जो निरंतर डेटा को इंगित करते हैं। – Kevin

0

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

+2

यह ज्यादातर समय सच हो सकता है। लेकिन अपवाद हैं। – Kevin

+1

वेटर्स वैसे भी, एक कॉन्स सेटर बहुत समझ में नहीं आता है;) – jwfearn

+0

मेरा मतलब था कि गैर-कॉन्स गेटर प्रभावी ढंग से एक सेटटर है। :) – Dima

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

स्कॉट मेयर्स का पालन करने के लिए किसी को भी कभी नहीं निकाल दिया गया :-) –

+0

const_cast का लगभग कभी भी उपयोग नहीं किया जाना चाहिए। यह कॉन्स-नेस तोड़ता है। यह embeeded सिस्टम पर especialy सच है जहां आपके पास रोम है। सामान्य रूप से const_cast का उपयोग करना एक बुरा विचार है। – Ted

+4

ओच ... अगर आप const == ROM पर गिन रहे हैं तो आप पहले से ही परेशानी में हो सकते हैं।कंपाइलर्स इतने मजबूत दावे को साबित करने के लिए पर्याप्त नहीं हैं। –

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 की आवश्यकता होती है (...)। यदि बाद के डेवलपर्स द्वारा दुरुपयोग किया जाता है, तो यह अभी भी यूबी का नेतृत्व कर सकता है। यदि वापस लौटाई जाने वाली चीज़ 'उत्परिवर्तनीय' है, तो यह एक अच्छा समाधान है। – Kevin

+0

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

+0

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

3

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

+0

अपरिभाषित व्यवहार से बचने के लिए आपको अभी भी एक const_cast की आवश्यकता है। मार्टिन यॉर्क और मेरी टिप्पणी के जवाब देखें। – Kevin

+1

केविन, मार्टिन यॉर्क –

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

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

+0

आह हाँ अच्छा विचार! मुझे हेडर में दिखाई देने वाली टेम्पलेट सामग्री पसंद नहीं है, लेकिन यदि यहां से यह संभावित रूप से लागू करने के लिए काफी सरल बनाता है तो यह शायद इसके लायक है। इस समाधान के लिए –

+0

+ 1 जो किसी भी कोड को डुप्लिकेट नहीं करता है, न ही किसी भी बदसूरत 'const_cast' का उपयोग करता है (जो गलती से ऐसा कुछ करने के लिए उपयोग किया जा सकता है जो _actually_ को ऐसा कुछ नहीं माना जाता है)। – HelloGoodbye

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' नहीं है। – TBBle

+0

@ टीबीबीएल कैसे? http://www.cplusplus.com/reference/type_traits/remove_const/ –

+0

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

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() -> ऑटो (कॉन्स), ऑटो (&&) = हटाएं; ' – kfsone

+0

gd1: ठीक है जो मुझे दिमाग में था। @kfsone और वास्तव में मैंने भी क्या निष्कर्ष निकाला। –

+1

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

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 के समान जवाब, मैं उसका उत्तर उखाड़ दूंगा क्योंकि मुझे लगता है कि यह – dats

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

करने का सही तरीका है लेकिन फिर आपको बैकस्लाश (मल्टीलाइन मैक्रोज़ के लिए सामान्य रूप से) के साथ सावधान रहना होगा और इसके अतिरिक्त आप अधिकांश में सिंटैक्स हाइलाइटिंग खो देंगे (अगर सभी नहीं) संपादक। – Ruslan

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)। – s3rvac

+0

@ s3rvac: धन्यवाद, तय –

संबंधित मुद्दे