std :: मानचित्र डिफ़ॉल्ट मान


86

जब कोई कुंजी मौजूद नहीं होती है तो क्या डिफ़ॉल्ट मूल्य std::mapके operator[]रिटर्न को निर्दिष्ट करने का कोई तरीका है?

जवाबों:


56

नहीं, वहाँ नहीं है। सबसे सरल उपाय यह करने के लिए अपने स्वयं के निशुल्क टेम्पलेट फ़ंक्शन को लिखना है। कुछ इस तरह:

#include <string>
#include <map>
using namespace std;

template <typename K, typename V>
V GetWithDef(const  std::map <K,V> & m, const K & key, const V & defval ) {
   typename std::map<K,V>::const_iterator it = m.find( key );
   if ( it == m.end() ) {
      return defval;
   }
   else {
      return it->second;
   }
}

int main() {
   map <string,int> x;
   ...
   int i = GetWithDef( x, string("foo"), 42 );
}

C ++ 11 अद्यतन

उद्देश्य: सामान्य सहयोगी कंटेनरों के साथ-साथ वैकल्पिक तुलनित्र और आवंटन मापदंडों के लिए खाता।

template <template<class,class,class...> class C, typename K, typename V, typename... Args>
V GetWithDef(const C<K,V,Args...>& m, K const& key, const V & defval)
{
    typename C<K,V,Args...>::const_iterator it = m.find( key );
    if (it == m.end())
        return defval;
    return it->second;
}

1
अच्छा समाधान है। आप कुछ टेम्पलेट तर्क जोड़ना चाह सकते हैं ताकि फ़ंक्शन टेम्पलेट उन मानचित्रों के साथ काम करे जो तुलनित्र और आवंटन के लिए डिफ़ॉल्ट टेम्पलेट मापदंडों का उपयोग नहीं करते हैं।
sbi

3
+1, लेकिन operator[]डिफ़ॉल्ट मान के साथ सटीक व्यवहार प्रदान करने के लिए , डिफ़ॉल्ट मान को if ( it == m.end() )ब्लॉक के अंदर के मानचित्र में डाला जाना चाहिए
David Rodríguez - dribeas

12
@ मैं मान रहा हूँ कि ओपी वास्तव में वह व्यवहार नहीं चाहता है। मैं कॉन्फ़िगरेशन पढ़ने के लिए एक समान योजना का उपयोग करता हूं, लेकिन मैं चाहता हूं कि कॉन्फ़िगरेशन अपडेट न हो अगर कोई कुंजी गायब है।

2
@ गमन बूल मापदंडों को कुछ लोगों द्वारा गलत शैली माना जाता है क्योंकि आप कॉल को देखकर नहीं बता सकते हैं (घोषणा के विपरीत) वे क्या करते हैं - इस मामले में "सही" का अर्थ है "डिफ़ॉल्ट का उपयोग करें" या "डॉन" t डिफ़ॉल्ट का उपयोग करें "(या पूरी तरह से कुछ और)? एक एनुम हमेशा स्पष्ट होता है लेकिन निश्चित रूप से अधिक कोड है। मैं इस विषय पर दो दिमागों में हूं।

2
यदि डिफ़ॉल्ट मान nullptr है, लेकिन यह उत्तर काम नहीं करता है, लेकिन stackoverflow.com/a/26958878/297451 करता है।
जॉन

44

हालांकि यह इस सवाल का बिल्कुल जवाब नहीं देता है, मैंने समस्या को इस तरह कोड के साथ दरकिनार कर दिया है:

struct IntDefaultedToMinusOne
{
    int i = -1;
};

std::map<std::string, IntDefaultedToMinusOne > mymap;

1
यह मेरे लिए सबसे अच्छा समाधान है। लागू करने में आसान, बहुत लचीला, और सामान्य।
इस्क

11

C ++ मानक (23.3.1.2) निर्दिष्ट करता है कि नव सम्मिलित मूल्य डिफ़ॉल्ट रूप से निर्मित है, इसलिए mapस्वयं इसे करने का एक तरीका प्रदान नहीं करता है। आपकी पसंद हैं:

  • वैल्यू टाइप को डिफॉल्ट कंस्ट्रक्टर दें जो इसे उस वैल्यू के लिए इनिशियलाइज़ करता है जो आप चाहते हैं, या
  • अपने स्वयं के वर्ग में मानचित्र को लपेटें जो डिफ़ॉल्ट मान प्रदान करता है और operator[]उस डिफ़ॉल्ट को सम्मिलित करने के लिए लागू करता है ।

8
ठीक है, सटीक होने के लिए नव सम्मिलित मूल्य का मूल्य इनिशियलाइज़्ड (8.5.5) है: - यदि T एक उपयोगकर्ता-घोषित कंस्ट्रक्टर (12.1) के साथ एक वर्ग प्रकार है, तो T के लिए डिफ़ॉल्ट कंस्ट्रक्टर को बुलाया जाता है (और इनिशियलाइज़ेशन बीमार है) - यदि टी में कोई सुलभ डिफ़ॉल्ट कंस्ट्रक्टर नहीं है); - यदि टी एक उपयोगकर्ता-घोषित निर्माता के बिना एक गैर-संघ श्रेणी का प्रकार है, तो टी के प्रत्येक गैर-स्थैतिक डेटा सदस्य और बेसकलेस घटक का मूल्य-प्रारंभिक है; - यदि टी एक सरणी प्रकार है, तो प्रत्येक तत्व मूल्य-प्रारंभिक है; - अन्यथा, वस्तु शून्य-आरंभीकृत है
तेडुसेज़ कोपेक

6

अधिक सामान्य संस्करण, समर्थन C ++ 98/03 और अधिक कंटेनर

सामान्य सहयोगी कंटेनरों के साथ काम करता है, एकमात्र टेम्पलेट पैरामीटर कंटेनर प्रकार ही है।

समर्थित कंटेनर: std::map, std::multimap, std::unordered_map, std::unordered_multimap, wxHashMap, QMap, QMultiMap, QHash, QMultiHash, आदि

template<typename MAP>
const typename MAP::mapped_type& get_with_default(const MAP& m, 
                                             const typename MAP::key_type& key, 
                                             const typename MAP::mapped_type& defval)
{
    typename MAP::const_iterator it = m.find(key);
    if (it == m.end())
        return defval;

    return it->second;
}

उपयोग:

std::map<int, std::string> t;
t[1] = "one";
string s = get_with_default(t, 2, "unknown");

यहाँ एक आवरण वर्ग का उपयोग करके एक समान कार्यान्वयन है, जो कि पायथन में विधि get()के dictप्रकार के समान है : https://github.com/hltj/wxMEdit/blob/master/src/xm/xm_nils.hpp

template<typename MAP>
struct map_wrapper
{
    typedef typename MAP::key_type K;
    typedef typename MAP::mapped_type V;
    typedef typename MAP::const_iterator CIT;

    map_wrapper(const MAP& m) :m_map(m) {}

    const V& get(const K& key, const V& default_val) const
    {
        CIT it = m_map.find(key);
        if (it == m_map.end())
            return default_val;

        return it->second;
    }
private:
    const MAP& m_map;
};

template<typename MAP>
map_wrapper<MAP> wrap_map(const MAP& m)
{
    return map_wrapper<MAP>(m);
}

उपयोग:

std::map<int, std::string> t;
t[1] = "one";
string s = wrap_map(t).get(2, "unknown");

MAP :: mapped_type & typename से लौटने के लिए सुरक्षित नहीं है MAP :: mapped_type & defval गुंजाइश से बाहर हो सकता है।
जो सी

6

C ++ 17 प्रदान करता है try_emplaceजो वास्तव में ऐसा करता है। यह मान निर्माणकर्ता के लिए एक कुंजी और एक तर्क सूची लेता है और एक जोड़ी देता है: iteratora bool: a ।: Http://en.cppreference.com/w/cpp/container/map/try_emplace


5

डिफ़ॉल्ट मान निर्दिष्ट करने का कोई तरीका नहीं है - यह हमेशा डिफ़ॉल्ट (शून्य पैरामीटर कंस्ट्रक्टर) द्वारा निर्मित मूल्य है।

वास्तव में operator[]संभवतः आपकी अपेक्षा से अधिक होता है जैसे कि मानचित्र में दी गई कुंजी के लिए कोई मान मौजूद नहीं है, यह डिफ़ॉल्ट कंस्ट्रक्टर से मान के साथ एक नया सम्मिलित करेगा।


2
सही, नई प्रविष्टियों को जोड़ने से बचने के लिए आप उपयोग कर सकते हैं findजो किसी दिए गए कुंजी के लिए कोई तत्व मौजूद होने पर अंतिम पुनरावृत्ति वापस नहीं करता है।
थॉमस स्काउब

@ThomasSchaub findउस मामले में समय की जटिलता कितनी है ?
ajaysinghnegi

5
template<typename T, T X>
struct Default {
    Default () : val(T(X)) {}
    Default (T const & val) : val(val) {}
    operator T & () { return val; }
    operator T const & () const { return val; }
    T val;
};

<...>

std::map<KeyType, Default<ValueType, DefaultValue> > mapping;

3
फिर इसे संशोधित करें ताकि यह काम करे। मैं इस केस को ठीक करने के लिए परेशान नहीं होने जा रहा हूँ, यह कोड पूरा करने के लिए डिज़ाइन नहीं किया गया था।
थॉमस एडिंग

3

मान का उपयोग डिफ़ॉल्ट निर्माणकर्ता द्वारा किया जाता है, जैसा कि अन्य उत्तर कहते हैं। हालाँकि, यह जोड़ना उपयोगी है कि साधारण प्रकार (इंटीग्रल टाइप जैसे इंट, फ्लोट, पॉइंटर या पीओडी (प्लान पुराने डेटा) प्रकार) के मामले में, मान शून्य-आरंभीकृत होते हैं (या मूल्य-आरंभीकरण द्वारा शून्य होते हैं (जो प्रभावी रूप से होते हैं) एक ही चीज), C ++ के किस संस्करण पर निर्भर करता है)।

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

std::map<int, char*> map;
typedef char *P;
char *p = map[123],
    *p1 = P(); // map uses the same construct inside, causes zero-initialization
assert(!p && !p1); // both will be 0

देखें कि क्या प्रकार के नाम के बाद कोष्ठक नए के साथ अंतर करते हैं? मामले पर अधिक जानकारी के लिए।


2

map::at()इसके बजाय एक वर्कअराउंड का उपयोग करना है []। यदि कोई कुंजी मौजूद नहीं है, तो atएक अपवाद फेंकता है। यहां तक ​​कि अच्छे, यह वैक्टर के लिए भी काम करता है, और इस प्रकार जेनेरिक प्रोग्रामिंग के लिए अनुकूल है जहां आप वेक्टर के साथ मानचित्र को स्वैप कर सकते हैं।

अपंजीकृत कुंजी के लिए एक कस्टम मान का उपयोग करना खतरनाक हो सकता है क्योंकि कस्टम मूल्य (जैसे -1) को कोड में और नीचे संसाधित किया जा सकता है। अपवादों के साथ, बग को स्पॉट करना आसान है।


1

हो सकता है कि आप एक कस्टम आवंटनकर्ता दे सकते हैं जो आप चाहते हैं कि डिफ़ॉल्ट मान के साथ आवंटित करें।

template < class Key, class T, class Compare = less<Key>,
       class Allocator = allocator<pair<const Key,T> > > class map;

4
operator[]कोई वस्तु T()जो आबंटक द्वारा की जाती है , को लागू करके बनाई गई वस्तु को लौटाती है।
sbi

1
@ एसएसबी: क्या नक्शा आवंटनकर्ताओं की constructपद्धति को नहीं बताता है ? मुझे लगता है कि इसे बदलना संभव होगा। मुझे constructउस फ़ंक्शन पर संदेह है जो कुछ और से अलग new(p) T(t);नहीं है, हालांकि यह अच्छी तरह से गठित नहीं है, हालांकि। संपादित करें: मूर्खता में, जो मूर्ख था, अन्यथा सभी मूल्य समान होंगे: पी कहां है मेरी कॉफी ...
GMANNICKG

1
@ मन: C ++ 03 की मेरी प्रति (23.3.1.2 में) कहती है कि operator[]लौटती है (*((insert(make_pair(x, T()))).first)).second। इसलिए जब तक मुझे कुछ याद नहीं आ रहा है, यह जवाब गलत है।
sbi

तुम सही हो। लेकिन यह मुझे गलत लगता है। वे डालने के लिए आवंटन फ़ंक्शन का उपयोग क्यों नहीं करते हैं?
VDVLeon

2
@ एसबीआई: नहीं, मैं मानता हूं कि यह जवाब गलत है, लेकिन एक अलग कारण से। संकलक वास्तव insertमें एक के साथ करता है T(), लेकिन अंदर है जब यह एक नई के लिए आवंटन प्राप्त स्मृति का उपयोग करेगा Tतब constructउस मेमोरी पर कॉल करें जिस पैरामीटर के साथ यह दिया गया था, जो है T()। इसलिए वास्तव में यह संभव है कि व्यवहार को बदलने के operator[]लिए इसे कुछ और लौटाया जाए, लेकिन आवंटनकर्ता इसे अलग नहीं कर सकता कि इसे क्यों कहा जा रहा है। इसलिए भले ही हमने constructइसे अनदेखा कर दिया हो, लेकिन यह पैरामीटर का उपयोग करता है और हमारे विशेष मूल्य का उपयोग करता है, इसका मतलब है कि प्रत्येक निर्मित तत्व में वह मूल्य होता है, जो खराब है।
GManNickG

1

Https://stackoverflow.com/a/2333816/272642 के उत्तर पर विस्तार करते हुए , यह टेम्प्लेट फंक्शन टाइप करने के लिए और टाइप करने के लिए std::map's' key_typeऔर ' mapped_typetypeedefs ' का उपयोग करता है । यह इन typedefs के बिना कंटेनरों के साथ काम नहीं करता है।keydef

template <typename C>
typename C::mapped_type getWithDefault(const C& m, const typename C::key_type& key, const typename C::mapped_type& def) {
    typename C::const_iterator it = m.find(key);
    if (it == m.end())
        return def;
    return it->second;
}

यह आपको उपयोग करने की अनुमति देता है

std::map<std::string, int*> m;
int* v = getWithDefault(m, "a", NULL);

जैसे तर्क देने की आवश्यकता के बिना std::string("a"), (int*) NULL


1

का उपयोग करें std::map::insert()

यह महसूस करते हुए कि मुझे इस पार्टी में काफी देर हो चुकी है, लेकिन यदि आप operator[]कस्टम डिफॉल्ट्स के साथ व्यवहार में रुचि रखते हैं (अर्थात, दिए गए कुंजी के साथ तत्व को ढूंढें, यदि वह मौजूद नहीं है तो मैप में एक तत्व डालें डिफ़ॉल्ट मान चुना गया है और या तो नए सम्मिलित मूल्य या मौजूदा मूल्य का संदर्भ लौटाता है), C ++ 17 से पहले ही आपके पास एक फ़ंक्शन उपलब्ध है std::map::insert():। insertयदि कुंजी पहले से मौजूद है, तो वास्तव में सम्मिलित नहीं करेंगे, बल्कि मौजूदा मान में एक पुनरावृत्तिकर्ता को लौटा देंगे।

कहो, आप स्ट्रिंग-टू-इंट का एक नक्शा चाहते थे और यदि कुंजी अभी तक मौजूद नहीं थी, तो 42 का डिफ़ॉल्ट मान सम्मिलित करता है:

std::map<std::string, int> answers;

int count_answers( const std::string &question)
{
    auto  &value = answers.insert( {question, 42}).first->second;
    return value++;
}

int main() {

    std::cout << count_answers( "Life, the universe and everything") << '\n';
    std::cout << count_answers( "Life, the universe and everything") << '\n';
    std::cout << count_answers( "Life, the universe and everything") << '\n';
    return 0;
}

जिसमें 42, 43 और 44 का उत्पादन होना चाहिए।

यदि मानचित्र मान के निर्माण की लागत अधिक है (यदि कुंजी को कॉपी करना / स्थानांतरित करना या मूल्य प्रकार महंगा है), तो यह एक महत्वपूर्ण प्रदर्शन दंड के रूप में आता है, जो मुझे लगता है कि C ++ 17 के साथ परिवृत्त होगा try_emplace

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.