Question

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:

  1. 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)?

  2. 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!

Was it helpful?

Solution

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.

OTHER TIPS

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.

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