2016-02-09 3 views
3

Если у меня есть функция find, которая иногда может не найти нужную вещь, я стараюсь, чтобы эта функция возвращала указатель таким образом, что nullptr указывает, что вещь не была найденный.Зачем использовать boost :: optional, когда я могу вернуть указатель

E.g.

Student* SomeClass::findStudent(/** some criteria. */) 

Если студент существует, то он будет возвращать указатель на найденный Student объекта, в противном случае он будет возвращать nullptr.

Я также видел boost::optional для этой цели. Например. When to use boost::optional and when to use std::unique_ptr in cases when you want to implement a function that can return "nothing"?

Вопрос в том, не возвращает указателю лучшее решение в этом случае. Т.е. существует вероятность того, что запрошенный элемент не будет найден, и в этом случае возвращение nullptr является идеальным решением. В чем преимущество использования чего-то вроде boost::optional (или любого другого подобного решения)?

Обратите внимание, что в моем примере findStudent только вернет указатель на объект, принадлежащий SomeClass.

+2

Как насчет того случая, когда функция 'find' возвращает тип значения, например. 'int' или' double'? – CompuChip

+0

Кроме того, из вашего связанного вопроса http://stackoverflow.com/a/14360932/2920343 – CompuChip

+0

@CompuChip Это имеет смысл? Вы просто вернете двойную ценность? Если вы хотите, чтобы пользователь имел возможность редактировать отдельный двойной номер в памяти, принадлежащей SomeClass, тогда вы снова вернете double *. – nappyfalcon

ответ

8

Преимущество возвращаемого здесь типа optional<Student&> заключается в том, что семантика использования очевидна для всех пользователей, знакомых с optional (и станет очевидным после их ознакомления с ним). Эти семантики:

  • Абонент не является владельцем Student и не несет ответственности за управление памятью. Вызывающий объект просто получает ссылку на существующий объект.
  • Понятно, что эта функция может выйти из строя. Вы возможно получите значение, а вы возможно ничего не получите. Понятно, что вызывающему нужно проверить результат так или иначе.

optional<T> является самодокументированным таким образом, что T* нет. Кроме того, он имеет другие преимущества, поскольку он может работать в тех случаях, когда вы хотите вернуть какой-либо тип объекта без необходимости его выделения. Что делать, если вам необходимо вернуть int или double или SomePOD?

+1

'optional ' явно запрещен в текущей версии TS типа. 'optional' - для типов значений. –

+0

@NicolBolas Почему? – Barry

+0

Hm, что потребует 'необязательный >', то. Некрасиво. – Angew

1

Предположим, у вас есть std::map<IndexType, ValueType>, где вы пытаетесь найти что-то (Примечание. То же самое относится к другим контейнерам, это просто для примера). У вас есть следующие варианты:

  • Вы возвращающих ValueType&: Пользователь может изменить вашу карту-контента и не нужно думать о памяти выделения/открепления. Но если вы не найдете ничего на своей карте, вам нужно выбросить исключение или что-то подобное.
  • Вы возвращаете ValueType*: Пользователь может изменить ваше содержимое карты, и вы можете вернуть nullptr, если вы ничего не найдете. Но пользователь может вызвать delete на этом указателе, и вы должны указать так или иначе, если он это сделает или нет.
  • Вы возвращаете умный указатель на ValueType: пользователю не нужно беспокоиться о удалении или не удалять и изменять содержимое карты в зависимости от типа смарт-указателя. Вы также можете вернуть nullptr. Но это в значительной степени требует от вас иметь дело с smart_pointers на вашей карте, что слишком сложно, если бы ValueType было бы e.г. просто int в противном случае.
  • Вы возвращаете простой ValueType: Пользователь не может изменять ваше картографическое содержание и не должен думать о распределении/освобождении памяти. Но если вы не найдете что-либо на своей карте, вам нужно вернуть специальный номер ValueType, который говорит пользователю, что вы ничего не нашли. В случае, если ваш ValueType является, например, int, который вы бы вернули, что делает ясным «нет int found».
  • Вы возвращаетесь подталкивание :: опциональный, который является ближайшим вы можете получить простой ValueType возврата по значению с дополнительной опцией «не возвращая ValueType»
3

optional<T&> действительно может быть заменен T* но T* не имеет четкой семантики (право собственности?).

Но optional<T> не может быть заменено на T*. Например:

optional<Interval> ComputeOverlap(const Interval&, const Interval&); 

Если не перекрываются, нет проблем с T* (nullptr) или optional<T>. Но если есть перекрытие, нам нужно создать новый интервал. Мы можем вернуть smart_pointer в этом случае или необязательно.

3

optional<T&> был удален из дорожки стандартизации С ++, потому что его использование вызывает сомнения: он ведет себя почти идентично не-обладания T* с немного отличается (и смешения отличается от optional<T> и T*) семантики.

optional<T&> в основном не владеющий T* завернутый симпатичный, и несколько странно.


Теперь, optional<T> - это отличное зверь.

Я использовал optional<Iterator> в своих алгоритмах поиска на основе контейнеров. Вместо того, чтобы возвращать end(), я возвращаю пустую опцию. Это позволяет пользователям определить без сравнения, если они не смогли найти элемент, и позволяет такой код:

if(linear_search_for(vec, item)) 

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

Указатели на элементы не дают вам информации о местоположении, которую вы можете захотеть, за исключением смежных контейнеров.

Итак, здесь я создал нулевой итератор, который имеет преимущества итераторов (в основном работающих с различными типами контейнеров) и указателей (может быть проверен на нулевое состояние).

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

Rect GetRect(); 

сейчас, это здорово. Но что, если вопрос может быть бессмысленным? Ну, один из подходов - вернуть пустой прямоугольник или другое значение «флаг».

Дополнительно вы можете сообщить, что он может вернуть прямой или ничего, а не использовать пустой прямоугольник для состояния «ничего». Он возвращает значение null.

int GetValue(); 

- лучший пример. Недопустимое значение может использовать состояние флага int - say -1 - но это заставляет каждого пользователя вашей функции искать и отслеживать состояние флага, а не случайно относиться к нему как к нормальному состоянию.

Вместо этого optional<int> GetValue() дает понять, что он может не работать, и. Если он заполнен, вы знаете, что это реальное значение, а не значение флага.

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

Опция - это типы значений с нулевым значением. Когда вы хотите управлять ресурсами локально, и вы все еще хотите пустое состояние, они дают понять.


Еще одна вещь, которую нужно изучить, - предлагаемый тип expected. Это необязательно, но когда в пустом состоянии содержится причина, почему она пуста.