2010-03-17 19 views
7

यह सवाल सामान्य रूप से सी ++ के बारे में अधिक उचित रूप से पूछा जा सकता है, लेकिन जैसा कि मैं लिनक्स पर जीसीसी का उपयोग कर रहा हूं, यह संदर्भ है। निम्नलिखित कार्यक्रम पर विचार करें:सी ++: ऑपरेटर [] पर पहुंचने पर जीसीसी कॉन्स पर गैर-कॉन्स क्यों पसंद करता है?

#include <iostream> 
#include <map> 
#include <string> 

using namespace std; 

template <typename TKey, typename TValue> 
class Dictionary{ 
    public: 
    map<TKey, TValue> internal; 

    TValue & operator[](TKey const & key) 
    { 
     cout << "operator[] with key " << key << " called " << endl; 
     return internal[key]; 
    } 

    TValue const & operator[](TKey const & key) const 
    { 
     cout << "operator[] const with key " << key << " called " << endl; 
     return internal.at(key); 
    } 

}; 

int main(int argc, char* argv[]) 
{ 
    Dictionary<string, string> dict; 
    dict["1"] = "one"; 
    cout << "first one: " << dict["1"] << endl; 

    return 0; 
} 

जब कार्यक्रम को क्रियान्वित करने, उत्पादन होता है:

operator[] with key 1 called 
    operator[] with key 1 called 
    first one: one 

मैं चाहते हैं क्या संकलक दूसरी कॉल में बजाय operator[]const विधि का चयन करने के लिए है। इसका कारण यह है कि पहले [1 1]] इस्तेमाल किए बिना, operator[] पर कॉल करने के लिए आंतरिक मानचित्र उस डेटा को बनाने का कारण बनता है जो अस्तित्व में नहीं है, भले ही केवल एक चीज जो मैं चाहता था वह कुछ डीबगिंग आउटपुट करना था, जो निश्चित रूप से है एक घातक आवेदन त्रुटि।

व्यवहार मैं सी # सूचकांक ऑपरेटर जो एक बात और एक सेट संचालन और जहां गेटर कुछ है कि अस्तित्व में नहीं है का उपयोग करने की कोशिश करता है तो आप एक अपवाद फेंक सकता है की तरह कुछ हो जाएगा रहा हूँ:

class MyDictionary<TKey, TVal> 
{ 
    private Dictionary<TKey, TVal> dict = new Dictionary<TKey, TVal>(); 
    public TVal this[TKey idx] 
    { 
     get 
     { 
      if(!dict.ContainsKey(idx)) 
       throw KeyNotFoundException("..."); 

      return dict[idx]; 
     } 
     set 
     { 
      dict[idx] = value; 
     } 
    } 
} 

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

उत्तर

0

यह उस स्थिति के कॉन्स्ट ऑब्जेक्ट को तत्काल करने के मामले में कॉन्स्ट विधि का उपयोग करेगा।

1

आप जो प्रभाव चाहते हैं उसे प्राप्त नहीं कर सकते हैं। जब dict non-const है, तो यह ऑपरेटर के गैर-कॉन्स संस्करण को कॉल करेगा []।

सी # इस मामले में बेहतर है क्योंकि यह निर्धारित कर सकता है कि 'dict [n]' असाइनमेंट का हिस्सा है या नहीं। सी ++ ऐसा नहीं कर सकता है।

+0

यह गैर-कॉन्स को तब तक कॉल करेगा जब तक कि एकमात्र संस्करण उपलब्ध न हो और जिसके परिणामस्वरूप "क्वालिफ़ायर को छोड़ दें" त्रुटि न हो। जाहिर है, संकलक जानता है कि कौन से संस्करण मौजूद हैं, इसलिए यह मेरे डेटा को नष्ट नहीं करने वाले व्यक्ति का भी उपयोग कर सकता है। मुझे व्यक्तिगत रूप से लगता है कि यह खराब संकलक व्यवहार है, इसलिए मुझे प्रबुद्ध करें। उन्होंने ऐसा क्यों किया जब दूसरी तरफ (गैर-कॉन्स को पसंद करने के लिए) सुरक्षित AFAICT है? – JonasW

+3

सुरक्षित प्रतीत हो सकता है, लेकिन यह बहुत तार्किक नहीं है, * सर्वोत्तम * मिलान गैर-आधार है क्योंकि यह सटीक उसी प्रकार और स्थिरता का है। इसके बाद यह अन्य मैचों की तलाश कर सकता है। आम तौर पर यह कम से कम आश्चर्य में परिणाम। यदि आप कॉन्स सुरक्षा चाहते हैं, तो आपको इसे अपने समय के लिए –

0

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

सी # में गुण और सी ++ में विधि कॉन्स्टेस के आधार पर ओवरलोडिंग अलग-अलग चीजें होती हैं जो एक अलग उद्देश्य की सेवा करती हैं।


मुझे आश्चर्य है अगर आपके प्रश्न Why can't we have an immutable version of operator[] for map से संबंधित है?

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

#include <iostream> 
#include <map> 
#include <string> 
#include <stdexcept> 

using namespace std; 

template <typename KeyType, typename ValueType> 
class DictProxy; 

template <typename KeyType, typename ValueType> 
class ConstDictProxy; 

template <typename TKey, typename TValue> 
class Dictionary{ 
    public: 
    map<TKey, TValue> internal; 

    DictProxy<TKey, TValue> operator[](TKey const & key); 
    ConstDictProxy<TKey, TValue> operator[](TKey const & key) const; 
}; 

template <typename KeyType, typename ValueType> 
class DictProxy 
{ 
    std::map<KeyType, ValueType>* p_map; 
    const KeyType* key; 
    DictProxy(std::map<KeyType, ValueType>* p_map, const KeyType* key): p_map(p_map), key(key) {} 
    friend class Dictionary<KeyType, ValueType>; 
public: 
    void operator=(const ValueType& value) const { 
     cout << "operator[] used on the left side of assignment with key " << *key << endl; 
     (*p_map)[*key] = value; 
    } 
    operator ValueType&() const { 
     cout << "operator[] used in a different context with " << *key << endl; 

     //you used at here 
     //it is in the C++0x standard, but generally not in online references? 
     typename std::map<KeyType, ValueType>::iterator it = p_map->find(*key); 
     if (it == p_map->end()) { 
      throw std::range_error("Missing key in map"); 
     } 
     return it->second; 
    } 
}; 

template <typename KeyType, typename ValueType> 
class ConstDictProxy 
{ 
    const std::map<KeyType, ValueType>* p_map; 
    const KeyType* key; 
    ConstDictProxy(const std::map<KeyType, ValueType>* p_map, const KeyType* key): p_map(p_map), key(key) {} 
    friend class Dictionary<KeyType, ValueType>; 
public: 
    operator const ValueType&() const { 
     cout << "operator[] const used in a different context with " << *key << endl; 
     typename std::map<KeyType, ValueType>::const_iterator it = p_map->find(*key); 
     if (it == p_map->end()) { 
      throw std::range_error("Missing key in map"); 
     } 
     return it->second; 
    } 
}; 

template <typename TKey, typename TValue> 
DictProxy<TKey, TValue> Dictionary<TKey, TValue>::operator[](TKey const & key) 
{ 
    return DictProxy<TKey, TValue>(&internal, &key); 
} 

template <typename TKey, typename TValue> 
ConstDictProxy<TKey, TValue> Dictionary<TKey, TValue>::operator[](TKey const & key) const 
{ 
    return ConstDictProxy<TKey, TValue>(&internal, &key); 
} 

int main(int argc, char* argv[]) 
{ 
    Dictionary<string, string> dict; 
    dict["1"] = "one"; 
    cout << "first one: " << string(dict["1"]) << endl; 

    const Dictionary<string, string>& r_dict = dict; 
    cout << "first one: " << string(r_dict["1"]) << endl; 
    return 0; 
} 

(कुछ कोड पुनः प्रयोग जब DictProxy और ConstDictProxy को लागू करने सूखी repect करने के लिए संभव होना चाहिए।)


हालांकि, अगर आपके प्रश्न से संबंधित है, तो समाधान IMO at() उपयोग करने के लिए है विधि जब आप डिफ़ॉल्ट मान जोड़ना नहीं चाहते हैं, और operator[] यदि आप करते हैं। मुझे संदेह है कि पूर्व सी ++ 0x के साथ एक जोड़ा है, यद्यपि?

+0

धन्यवाद का उपयोग करने के लिए निर्देशित करना होगा। मैं आज रात को देख लूंगा। – JonasW

0

बस सी ++ में याद रखें आप प्रभारी हैं और निर्णय लेने के लिए मिलता है।

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

तो हाँ .... आप संकलक को बताते हैं कि कॉन्स के रूप में क्या व्यवहार करना है।

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