Я читаю документацию, std::experimental::optional
и у меня есть хорошее представление о том, что он делает, но я не понимаю, когда мне следует его использовать или как я должен его использовать. На сайте пока нет примеров, поэтому мне сложнее понять истинную концепцию этого объекта. Когда - std::optional
хороший выбор для использования и как он компенсирует то, что не было найдено в предыдущем стандарте (C ++ 11).
c++
boost-optional
c++-tr2
0x499602D2
источник
источник
optional
. Представьте, что вы хотитеoptional<int>
или даже<char>
. Вы действительно думаете, что «дзен» требуется для динамического выделения, разыменования и последующего удаления - чего-то, что в противном случае не потребовало бы какого-либо выделения и компактно поместилось бы в стеке и, возможно, даже в регистре?std::optional
(хотя это может быть дляstd::unique_ptr
). Точнее, стандарт требует, чтобы T [...] удовлетворял требованиям Destructible .Ответы:
Самый простой пример, который я могу придумать:
То же самое может быть выполнено с помощью аргумента ссылки (как в следующей подписи), но использование
std::optional
делает подпись и использование более удобными.Другой способ сделать это особенно плох :
Это требует динамического распределения памяти, беспокойства о праве собственности и т. Д. - всегда предпочитайте одну из двух других подписей выше.
Другой пример:
Это крайне предпочтительно, чем иметь что-то вроде a
std::unique_ptr<std::string>
для каждого номера телефона!std::optional
дает вам локальность данных, которая отлично подходит для производительности.Другой пример:
Если поиск не содержит определенного ключа, мы можем просто вернуть «нет значения».
Я могу использовать это так:
Другой пример:
В этом гораздо больше смысла, чем, скажем, иметь четыре перегрузки функций, которые принимают все возможные комбинации
max_count
(или нет) иmin_match_score
(или нет)!Он также устраняет в проклятое «Pass
-1
для ,max_count
если вы не хотите предел» или «Passstd::numeric_limits<double>::min()
дляmin_match_score
если вы не хотите , минимальный балл»!Другой пример:
Если строки запроса нет
s
, я хочу «нетint
» - не какое-то особое значение, которое кто-то решил использовать для этой цели (-1?).Дополнительные примеры вы можете найти в
boost::optional
документации .boost::optional
и вstd::optional
основном будут идентичны с точки зрения поведения и использования.источник
std::optional<T>
- это просто aT
и abool
. Реализации функций-членов чрезвычайно просты. Производительность не должна быть проблемой при ее использовании - бывают случаи, когда что-то не является обязательным, и в этом случае это часто правильный инструмент для работы.std::optional<T>
намного сложнее. Он использует размещениеnew
и многое другое с правильным выравниванием и размером, чтобы,constexpr
помимо прочего, сделать его буквальным типом (то есть использовать с ). НаивностьT
иbool
подход быстро потерпели бы неудачу.union storage_t { unsigned char dummy_; T value_; ... }
Строка 289:struct optional_base { bool init_; storage_t<T> storage_; ... }
Как это не «а»T
и «аbool
»? Я полностью согласен с тем, что реализация очень сложна и нетривиальна, но концептуально и конкретно типом является aT
и abool
. «НаивностьT
иbool
подход быстро потерпят неудачу». Как вы можете сделать это заявление, глядя на код?struct{bool,maybe_t<T>}
объединение, которого нельзя делать,struct{bool,T}
что во всех случаях будет создавать букву T.std::unique_ptr<T>
иstd::optional<T>
вовсе в некотором смысле выполняют роль «необязательногоT
». Я бы описал разницу между ними как «детали реализации»: дополнительные выделения, управление памятью, локальность данных, стоимость перемещения и т. Д. Я бы никогда не стал,std::unique_ptr<int> try_parse_int(std::string s);
например, потому что это вызовет выделение для каждого вызова, даже если нет причин для , У меня никогда не было бы класса сstd::unique_ptr<double> limit;
- почему происходит распределение и потеря локальности данных?Пример цитируется из новой принятой статьи: N3672, std :: optional :
источник
int
иерархия вызовов вверх или вниз, вместо того, чтобы передавать какое-то «фантомное» значение, которое, как «предполагается», имеет значение «ошибка».str2int()
реализовать преобразование так, как он хочет, (B) независимо от метода полученияstring s
, и (C) передает полное значение черезoptional<int>
вместо глупогоbool
способа на основе магического числа, / ссылки или динамического распределения Делать это.Подумайте, когда вы пишете API и хотите заявить, что «отсутствие возврата» не является ошибкой. Например, вам нужно прочитать данные из сокета, и когда блок данных будет завершен, вы проанализируете его и вернете:
Если добавленные данные завершили анализируемый блок, вы можете его обработать; в противном случае продолжайте чтение и добавление данных:
Изменить: относительно остальных ваших вопросов:
Когда вы вычисляете значение и вам нужно его вернуть, это улучшает семантику для возврата по значению, чем для получения ссылки на выходное значение (которое может не быть сгенерировано).
Если вы хотите убедиться, что клиентский код должен проверять выходное значение (тот, кто пишет клиентский код, может не проверять наличие ошибок - если вы попытаетесь использовать неинициализированный указатель, вы получите дамп ядра; если вы попытаетесь использовать не- инициализированный std :: optional, вы получите перехватываемое исключение).
До C ++ 11 вам приходилось использовать другой интерфейс для «функций, которые могут не возвращать значение» - либо возвращать по указателю и проверять значение NULL, либо принимать выходной параметр и возвращать код ошибки / результата для «недоступно. ».
И то, и другое требует от разработчика клиента дополнительных усилий и внимания, чтобы сделать все правильно, и оба являются источником путаницы (первая заставляет разработчика клиента думать об операции как о выделении памяти и требует от клиентского кода реализации логики обработки указателя, а вторая позволяет клиентский код, чтобы избежать использования недопустимых / неинициализированных значений).
std::optional
хорошо заботится о проблемах, возникающих с предыдущими решениями.источник
boost::
вместоstd::
?boost::optional
потому что примерно через два года использования он жестко закодирован в моей префронтальной коре).Я часто использую необязательные параметры для представления дополнительных данных, извлеченных из файлов конфигурации, то есть где эти данные (например, с ожидаемым, но не обязательным элементом в XML-документе) необязательно предоставляются, чтобы я мог явно и четко показать, если данные фактически присутствовали в XML-документе. Особенно, когда данные могут иметь состояние «не установлено», а не «пусто» и «установлено» (нечеткая логика). С необязательным, установленным и не установленным ясно, также пустым будет ясно со значением 0 или нулем.
Это может показать, что значение «не установлено» не эквивалентно «пусто». По идее, указатель на int (int * p) может показать это, где null (p == 0) не установлен, значение 0 (* p == 0) установлено и пусто, а любое другое значение (* p <> 0) устанавливается в значение.
В качестве практического примера у меня есть фрагмент геометрии, извлеченный из XML-документа, который имеет значение, называемое флагами рендеринга, где геометрия может либо переопределить флаги рендеринга (установить), либо отключить флаги рендеринга (установлено в 0), либо просто не влияют на флаги рендеринга (не заданы), необязательный параметр - ясный способ представить это.
Очевидно, что указатель на int в этом примере может достичь цели или, что лучше, указатель на общий ресурс, поскольку он может предложить более чистую реализацию, однако я бы сказал, что в этом случае речь идет о ясности кода. Всегда ли нуль "не установлен"? С указателем это не ясно, поскольку null буквально означает, что он не выделен или не создан, хотя может , но не обязательно может означать «не установлен». Стоит отметить, что указатель должен быть освобожден, и в хорошей практике установлен на 0, однако, как и в случае с общим указателем, необязательный параметр не требует явной очистки, поэтому не стоит смешивать очистку с необязательный не был установлен.
Я считаю, что дело в ясности кода. Ясность снижает стоимость поддержки и разработки кода. Ясное понимание цели кода невероятно ценно.
Использование указателя для представления этого потребовало бы перегрузки концепции указателя. Чтобы представить «null» как «not set», обычно вы можете увидеть один или несколько комментариев в коде, объясняющих это намерение. Это неплохое решение вместо необязательного, однако я всегда предпочитаю неявную реализацию, а не явные комментарии, поскольку комментарии не подлежат исполнению (например, путем компиляции). Примеры этих неявных элементов для разработки (те статьи в разработке, которые предоставляются исключительно для обеспечения намерения) включают различные приведения в стиле C ++, «const» (особенно для функций-членов) и тип «bool», и это лишь некоторые из них. Возможно, вам действительно не нужны эти функции кода, если все подчиняются намерениям или комментариям.
источник