Unordered_map: что вернуть, если ключ отсутствует на карте?

Как предисловие к этому вопросу, я должен сказать, что я программист на Java и поэтому гораздо более привык к семантике Maps в Java, чем в С++. В Java это довольно часто и ожидается, что при поиске ключа на карте возвращается null. Я переводил часть нашего кода на С++ и пытаюсь найти способ выполнения С++ при взаимодействии с unordered_map.

В частности, у меня есть класс, который содержит unordered_map. Вместо того, чтобы подвергать карту непосредственно клиентскому коду, у меня есть две функции-обертки, одна для ввода пары ключ/значение в карту и одна для извлечения значения для указанного ключа, т.е.

void set_tag_value(string tag, string value);

string& get_tag_value(string tag);

Если я использую unordered_map.at() для извлечения значения, тогда он выкинет исключение, которое мой код должен будет поймать, или, альтернативно, разрешить ему распространять код клиента. (Однако распространение исключения на меня кажется недружелюбным).

Возможно, альтернативой было бы изменить возвращаемое значение на тип string* и вернуть NULL, если он не найден (что является способом Java для этого), но тогда пользователю нужно проверить значение NULL (что также не так дружелюбно).

Итак, мой вопрос состоит из двух частей:

  • Что такое удобный для разработчиков способ обработки неудачного поиска и какое возвращаемое значение было бы полезно (исключение, NULL, пустая строка или что-то еще)?

  • Внутри моего кода метод поиска карты более типичен для использования, если вы ожидаете, что он не сможет найти ключ, в() и исключение catch, или найти и проверить iterator == map.end()? (Эта часть вопроса заключается в том, что я просто пытаюсь изучить способ выполнения С++).

Спасибо за любой совет!

Ответ 1

По-моему, лучше не возвращать указатели на содержимое карты, которую вы сохраняете конфиденциально, поскольку указатели могут быть недействительными, если карта изменяется.

Я бы возвращал функцию код успеха (bool) и передавал ссылку на строку, чтобы фактически вернуть значение, если найдено. Например,

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;
}

Обратите внимание, что пока unordered_map::at() будет бросать, если ключ не найден, unordered_map::find() возвращает недопустимый итератор (unordered_map::end()), поэтому вы можете избежать обработки исключений таким образом.

Если вы хотите сохранить строку, просто верните пустую строку (return string();), если ключ не найден.

Ответ 2

Это два варианта, которые я бы рассматривал в зависимости от вашей готовности использовать boost:

Возвращает указатель:

/* const? */ string* get_tag_value_ptr(const string& tag)
{
    auto it = theMap.find(tag);
    if (it != theMap.end()) {
        return &it->second;
    }

    return nullptr;
}

Возвращает необязательную ссылку:

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;
}

Надеемся, что скоро будет std::optional, хотя он был отложен из С++ 14.

Для этих методов поиска нет необходимости копировать значение с карты. Возврат через параметр out означает создание копии значения. Я думаю, это зависит от ваших требований.