Question

I am trying to make use of operator[] in std::map to read elements using keys.

But when i am trying to access an invalid key it is throwing an exception which I am not able to catch using try-catch block .

Here is the code I am using:

class MapElement
{
 public:
     int a;
     char c;
};

int main()
{
   MapElement m1,m2,m3;
   m1.a =10;m1.c = 'a';
   m2.a =20;m2.c ='b';
   m3.a =30;m3.c ='c';

   map <char ,MapElement*> Mymap;
   map<char,MapElement*>::iterator iter = Mymap.begin();
   Mymap.insert(iter , std::pair<int, MapElement*>('1',&m1));
   Mymap.insert(iter , std::pair<int, MapElement*>('1',&m2));
   cout<<Mymap['1']->a;
   try
   {
      cout<<Mymap['2']->a;
   }
   catch(exception e)
   {
       cout<<e.what();
   }
   catch(...)
   {
        cout<< "unknown error";
   }
}

How can I handle this exception ?

Was it helpful?

Solution

The problem is being caused by std::map::operator[] creating a new entry for a key that does not exist:

Returns a reference to the value that is mapped to a key equivalent to key, performing an insertion if such key does not already exist.

In this case, the value is a pointer which will not be pointing to a valid MapElement. This is not a runtime failure but a programmer error and is causing undefined behaviour. Even it is possible to catch this type of error it should not be caught in a way that would permit the program to continue as the program may exhibit other unexpected behaviour.

Use std::map::at() if your compiler supports c++11:

try
{
    std::cout<< Mymap.at('2') << std::endl;
}
catch (std::out_of_range& const e)
{
    std::cerr << e.what() << std::endl;
}

(see http://ideone.com/FR4svY for an example). Otherwise, if your compiler does not support c++11 use std::map::find(), which does not throw an exception but returns std::map::end() if the map does not contain the requested key:

template <typename K, typename V>
V& map_at(std::map<K, V>& a_map, K const& a_key)
{
    typename std::map<K, V>::iterator i = a_map.find(a_key);
    if (a_map.end() == i)
    {
        throw std::out_of_range("map_at()");
    }
    return i->second;
}

try
{
    std::cout<< map_at(Mymap, '2') << std::endl;
}
catch (std::out_of_range& const e)
{
    std::cerr << e.what() << std::endl;
}

(see http://ideone.com/lIkTD3 for example).

OTHER TIPS

I suggest you to use find method and compare them with the map's end to hande unexisting keys:

map<char,MapElement*>::iterator iter = Mymap.find('2');
if (iter != Mymap.end()) {
    // do something if the key exist
} else {
    // do anythig if the key was not founded
}

The problem is that you are de-referencing a null pointer here, because calling operator[] with a non-existent key results in the creation of a new element with that key, and a value-initialized value type (in this case, a null MapElement*):

cout<<Mymap['2']->a; // The error is to call ->a, not Mymap['2']

This does not raise an exception, it is undefined behaviour. What you can do is call a method that raises an exception instead:

MapElement* m = Mymap.at('2'); // throws if there is no '2' element
cout << m->a;

Here, the call to at() will throw if there is no element with key '2'.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top