As a preface to this question, I have to say that I am a Java programmer and therefore much more accustomed to the semantics of Maps in Java than in C++. In Java it is quite common and expected get null
returned when looking up a key in the Map. I am translating some of our code to c++, and trying to find the c++ way of doing things when interacting with an unordered_map.
Specifically I have a class which contains an unordered_map. Instead of exposing the map directly to client code, I have 2 wrapper functions, one to put key/value pair into the map, and one to retrieve the value for specified key, i.e:
void set_tag_value(string tag, string value);
string& get_tag_value(string tag);
If I use unordered_map.at()
to retrieve the value, then it will throw an Exception which my code would need to catch, or alternatively, allow it propagate to the client code. (Propagating the exception to the seems unfriendly to me, though).
Maybe an alternative would be to change the return value to a string*
type, and return NULL if not found (which is the Java way of doing it), but then user needs to check for NULL (which is also not so friendly).
So my question has two parts:
What is developer-friendly way to handle the failed look up, and what return value would be useful (exception, NULL, empty string, or something else)?
Inside my code, which map look up method is more typical to use when you expect it might not find the key, at() and catch exception, or find and check iterator == map.end()? (This part of the question is me just trying to learn the c++ way of doing things).
Thanks for any advice!
the unordered map will try to default initialize mystruct with the key 'x' and assign to my struct. if u wanna avoid this, use . at(key) if it doesn't exist it will throw an out_of_range exception , which you can catch it and handle.
The map::operator[] searches the data structure for a value corresponding to the given key, and returns a reference to it. If it can't find one it transparently creates a default constructed element for it.
To check if a key exists in a C++ map, you can use std::map::count. It returns 0 (the key is absent) or 1 (the key is present).
In my opinion it's best not to return pointers to the contents of a map that you're keeping private since the pointers may be invalidated if the map changes.
I would have the function return a success code (bool
) and pass a reference to a string to actually return the value in if found. For example,
bool get_tag_value(const string& tag, string& value)
{
auto t = my_map.find(tag);
if (t == my_map.end()) return false;
value = t->second;
return true;
}
Note that while unordered_map::at()
will throw if the key is not found, unordered_map::find()
returns an invalid iterator (unordered_map::end()
) - so you can avoid handling exceptions this way.
If you want to stick with returning a string, then simply return an empty string (return string();
) if the key isn't found.
These are the 2 options I would consider depending on your willingness to use boost:
Return a pointer:
/* const? */ string* get_tag_value_ptr(const string& tag)
{
auto it = theMap.find(tag);
if (it != theMap.end()) {
return &it->second;
}
return nullptr;
}
Return an optional reference:
boost::optional</* const? */ string&> get_tag_value_opt(const string& tag)
{
auto it = theMap.find(tag);
if (it != theMap.end()) {
return it->second;
}
return boost::none;
}
Hopefully we will have std::optional
soon, although it has been postponed from C++14.
These methods of retrieval require no need to copy the value from the map. Returning via out parameter implies making a copy of the value. I guess it depends on what your requirements are.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With