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