unordered_map: что возвращать, если ключа нет на карте?

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

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

void set_tag_value(string tag, string value);

string& get_tag_value(string tag);

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

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

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

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

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

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


person Sam Goldberg    schedule 04.10.2013    source источник
comment
boost::optional или std::optional (С++ 14). Подробнее в моем ответе здесь.   -  person milleniumbug    schedule 05.10.2013
comment
Способ C++ состоял бы в том, чтобы предоставить клиенту доступ к полному интерфейсу unordered_map, чтобы они могли (например) применять к нему стандартные алгоритмы через его итераторы.   -  person Jerry Coffin    schedule 05.10.2013
comment
@JerryCoffin: я часто чувствую себя некомфортно, предоставляя прямой доступ к внутренним структурам данных класса, если только я не чувствую, что есть какой-то функциональный вариант использования клиентского кода для доступа к нему как к объекту. Конечно, в Java я часто обнаруживал, что по мере развития кода я меняю базовые структуры данных, и поэтому все, что с ними связано, также должно быть перекомпилировано. В этом случае клиенты не должны заботиться о том, как хранятся сопоставления, им просто нужно иметь возможность извлекать значения (и не иметь ошибки, если значение не найдено).   -  person Sam Goldberg    schedule 06.10.2013
comment
@SamGoldberg: Дело в том, что unordered_map уже предоставляет довольно абстрактный интерфейс, функциональный и хорошо спроектированный. Что еще более важно, каждый, кто знает C++, уже знает, как его использовать. Практически все, чего вы добиваетесь, скрывая это, — это создаете другой (менее функциональный) интерфейс для изучения вашими пользователями.   -  person Jerry Coffin    schedule 06.10.2013


Ответы (2)


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

Я бы заставил функцию возвращать код успеха (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();), если ключ не найден.

person Jonathan Potter    schedule 04.10.2013

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

Вернуть указатель:

/* 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, хотя он был перенесен с C++14.

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

person goji    schedule 04.10.2013
comment
Предложение по прокачке хорошее. В конце концов, я отказался от него, просто чтобы возвращаемое значение оставалось строкой для упрощения использования. В этом конкретном приложении пустая строка является разумным возвращаемым значением, подразумевающим пустоту. (Однако во многих других случаях boost::optional будет единственным выходом.) Спасибо за идеи. - person Sam Goldberg; 07.10.2013