Из того, что я понимаю, есть 2 * способа, которыми вы можете реализовать функцию, которая иногда не возвращает результат (например, человек, найденный в списке госзакупок).
* — мы игнорируем необработанную версию ptr, пару с флагом bool и исключение, когда ни одна версия не найдена.
boost::optional<Person> findPersonInList();
или же
std::unique_ptr<Person> findPersonInList();
Так есть ли причины отдавать предпочтение одному другому?
Это зависит от того, хотите ли вы вернуть справиться или копия.
Если вы хотите вернуть справиться:
Person*
boost::optional<Person&>
оба приемлемых варианта. Я склонен использовать Ptr<Person>
класс, который выбрасывает в случае нулевого доступа, но это моя паранойя.
Если вы хотите вернуть копия:
boost::optional<Person>
для неполиморфных классовstd::unique_ptr<Person>
для полиморфных классовпотому что динамическое выделение сопряжено с накладными расходами, поэтому вы можете использовать его только при необходимости.
Общий ответ таков: ваши намерения выражаются boost::optional
и не std::unique_ptr
, Тем не менее, ваш особый случай find
операция, вероятно, должна соответствовать стандартному способу работы библиотеки, предполагая, что ваш базовый тип имеет концепцию итераторов: вернуть итератор в end()
если элемент не найден, и итератор для элемента в противном случае.
boost::optional
более четко заявляет ваше намерение. Вам нужно явно документировать, что пусто std::unique_ptr
означает, что нет смысла возвращать
Существует четвертый способ сделать это: заставить функцию выдать исключение, если ничего не найдено.
Я знаю, что это на самом деле не отвечает на ваш вопрос, и поэтому я прошу прощения, но, возможно, вы не думали об этом.
Ах, Ксео еще не появился?
Ну, я сказал вам это однажды, и я скажу это снова: эти два совершенно разных объекта с разными целями.
unique_ptr
средства У меня есть объект. Это просто другой способ сказать: «Я объект». Таким образом, ноль unique_ptr
это то, что может привлечь внимание. Я ожидал предмета, но ничего не получил; код должен быть неправильным!optional
средства Иногда я могу быть не инициализирован, но это нормально. В этом случае нет никаких забот; если это None
это поведение, о котором думали.Тот факт, что оба неявно преобразуются в bool, не означает, что их можно использовать с перезарядкой. использование optional
с кодом, который, вероятно, не произведет никакого вывода (например, чтение потока). использование unique_ptr
на заводских объектах; они будут более вероятный создайте объект для вас, а если нет, бросьте исключение.
Вывод относительно вашего примера: find
должен вернуться optional
,
Концептуально сводится к этому, учитывая необнуляемое требование:
std::optional
имеет семантику значений, стековое хранилище.
std::unique_ptr
есть ход семантика, куча магазина.
Если вы хотите использовать семантику значений и хранилище кучи, используйте std::indirect
http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0201r1.pdf .
(Если вы хотите переместить семантику и хранилище стека … Я не знаю. Я думаю, что это unique_ptr с распределителем стека?)
Так есть ли причины отдавать предпочтение одному другому?
Они выражают совершенно разные намерения: optional
говорит, что функция может не дать результат, чтобы дать вам (и это не случай ошибки). unique_ptr
говорит вам кое-что о семантике владения (и более приемлемо для использования с нулем, чтобы выразить ошибку).
Обычно я использовал бы тот, который лучше всего выражает намерение интерфейса.
Например, предположим, что вы пишете HTTP-сервер, который пытается проанализировать полученный буфер в объект HTTP-запроса. Когда вы пытаетесь проанализировать неполный буфер, ошибки не возникает, вам просто нужно подождать и буферизовать больше данных, а затем повторить попытку.
Я бы выразил это с помощью optional
, чтобы было ясно, что функция может ничего не возвращать (и ничего не возвращать — это не ошибка).
В случае, если мой синтаксический анализ должен был проверить вещи (например, синтаксический анализатор регулярных выражений должен выдать ошибку, если проанализированное выражение недопустимо регулярное выражение), я возвратил бы нуль unique_ptr
или, еще лучше, выбросить исключение.