Я читаю документацию std::experimental::optional
, и у меня есть хорошее представление о том, что он делает, но я не понимаю , когда Я должен использовать его или как его использовать. На сайте пока нет каких-либо примеров, которые затрудняют мне понимание истинной концепции этого объекта. Когда std::optional
- хороший выбор для использования, и как он компенсирует то, что не было найдено в предыдущем стандарте (C++ 11).Как использовать std :: optional?
ответ
Простейший пример, который я могу думать:
std::optional<int> try_parse_int(std::string s)
{
//try to parse an int from the given string,
//and return "nothing" if you fail
}
То же самое может быть достигнуто с эталонным аргументом вместо (как показано в следующей подписи), но с использованием std::optional
делает подпись и использование приятнее.
bool try_parse_int(std::string s, int& i);
Другим способом, что это может быть сделано особенно плохо:
int* try_parse_int(std::string s); //return nullptr if fail
Это требует динамического выделения памяти, заботясь о собственности и т.д. - всегда предпочитает один из двух других подписей выше.
Другой пример:
class Contact
{
std::optional<std::string> home_phone;
std::optional<std::string> work_phone;
std::optional<std::string> mobile_phone;
};
Это крайне желательно иметь что-то вместо того, чтобы, как в std::unique_ptr<std::string>
для каждого номера телефона! std::optional
дает вам данные, которые отлично подходят для производительности.
Другого пример:
template<typename Key, typename Value>
class Lookup
{
std::optional<Value> get(Key key);
};
Если поиск не имеет определенный ключа в нем, то мы не можем просто вернуть «никакой ценности.»
я могу использовать его как это:
Lookup<std::string, std::string> location_lookup;
std::string location = location_lookup.get("waldo").value_or("unknown");
Другой пример:
std::vector<std::pair<std::string, double>> search(
std::string query,
std::optional<int> max_count,
std::optional<double> min_match_score);
Это делает намного больше смысла, чем, скажем, имея четыре перегруженные функции, которые принимают все возможные комбинации max_count
(или нет) и min_match_score
(или нет)!
Он также устраняетпроклят «Pass -1
для max_count
, если вы не хотите предел» или «Pass std::numeric_limits<double>::min()
для min_match_score
, если вы не хотите, минимальное количество баллов!»
Другой пример:
std::optional<int> find_in_string(std::string s, std::string query);
Если строка запроса не s
, я не хочу «не int
» - не независимо от особого значения кто-то решил использовать для этой цели (- 1?).
Дополнительные примеры можно посмотреть на boost::optional
documentation. boost::optional
и std::optional
будут в основном идентичными с точки зрения поведения и использования.
@ChrisCM: Это будет C способ сделать это. Путь C++ использовал бы умный указатель, чтобы избежать утечки памяти, или возвратил один из результатов через параметр handle. –
Какова производительность std :: optional
@gnzlbg 'std :: optional
Пример цитировал New adopted paper: N3672, std::optional:
optional<int> str2int(string); // converts int to string if possible
int get_int_from_user()
{
string s;
for (;;) {
cin >> s;
optional<int> o = str2int(s); // 'o' may or may not contain an int
if (o) { // does optional contain a value?
return *o; // use the value
}
}
}
И это считается хорошим примером? (видя, как это взято прямо из раздела обзора N3672). Как это дерьмо улучшается по сравнению с некоторым вариантом int i; while (! (cin >> i)) ;? Недоумение. – Wiz
Потому что вы можете _pass_ получить информацию о том, есть ли у вас иерархия «int» или не вверх или вниз, вместо того, чтобы передавать какое-то «фантомное» значение «предполагается» иметь значение «ошибка». –
@Wiz Это на самом деле отличный пример. Он (A) позволяет 'str2int()' реализовать преобразование, но он хочет, (B), независимо от метода получения 'string s', и (C) передает полный смысл через' optional
, но я не понимаю, когда я должен использовать его или как я должен использовать его.
Рассмотрите, когда вы пишете API, и хотите выразить, что значение «не имеет возврата» не является ошибкой. Например, вам нужно прочитать данные из сокета, и когда блок данных будет завершен, вы разобрать его и вернуть его:
class YourBlock { /* block header, format, whatever else */ };
std::optional<YourBlock> cache_and_get_block(
some_socket_object& socket);
Если прилагаемые данные завершены оформленной блок, вы можете обработать его; в противном случае, продолжайте читать и добавления данных:
void your_client_code(some_socket_object& socket)
{
char raw_data[1024]; // max 1024 bytes of raw data (for example)
while(socket.read(raw_data, 1024))
{
if(auto block = cache_and_get_block(raw_data))
{
// process *block here
// then return or break
}
// else [ no error; just keep reading and appending ]
}
}
Edit: относительно остальных ваших вопросов:
Когда это станд :: опциональный хороший выбор для использования
Когда вы вычисляете значение и должны его возвращать, он делает для лучшей семантики возврат по значению, чем для ссылки на выходное значение (которое не может быть сгенерировано).
Если вы хотите, чтобы убедиться, что код клиента имеет проверить выходное значение (кто пишет код клиента не может проверить наличие ошибок - если вы пытаетесь использовать не-инициализируется указатель вы получаете дамп, если вы пытаетесь использовать un-initialized std :: optional, вы получаете исключение, исключающее блокировку).
[...] и как это компенсировать то, что не было найдено в предыдущем стандарте (C++ 11).
Для того, чтобы перейти на C++ 11, вам пришлось использовать другой интерфейс для «функций, которые могут не возвращать значение» - либо вернуть указатель, либо проверить значение NULL, либо принять выходной параметр и вернуть ошибку/Код результата для "недоступен".
Оба налагают дополнительные усилия и внимание со стороны исполнителей-клиентов, чтобы получить их право, и оба являются источником путаницы (первое нажатие на клиента-исполнителя, чтобы думать о операции как о распределении и требовать, чтобы клиентский код реализовал логику обработки указателя а второй разрешающий код клиента уйти с использованием недопустимых/неинициализированных значений).
std::optional
прекрасно заботится о проблемах, связанных с предыдущими решениями.
Я знаю, что они в основном одинаковы, но почему вы используете 'boost ::' вместо 'std ::'? – 0x499602D2
Спасибо - я исправил его (я использовал 'boost :: optional', потому что примерно через два года его использования он жестко закодирован в моей передлоновой коре). – utnapistim
Это кажется мне плохим примером, поскольку, если несколько блоков были завершены, можно вернуть только один, а остальные отбросить. Вместо этого функция должна возвращать, возможно, пустую коллекцию блоков. –
Я часто использую опции для представления необязательных данных, извлеченных из файлов конфигурации, то есть, где эти данные (например, с ожидаемым, но необязательным элементом внутри XML-документа) предоставляется необязательно, так что я могу явно и ясно показать, действительно ли данные были представлены в документе XML. Особенно, когда данные могут иметь «не установленное» состояние по сравнению с «пустым» и «установленным» состоянием (нечеткой логикой). С опциональным, установленным и не установленным ясно, также пустое было бы ясным со значением 0 или null.
Это может показать, как значение «не установлено» не эквивалентно «пустому». По идее, указатель на int (int * p) может показать это, где null (p == 0) не задано, значение 0 (* p == 0) установлено и пустое, а любое другое значение (* p <> 0) устанавливается в значение.
Для практического примера у меня есть фрагмент геометрии, вытащенный из документа XML, который имеет значение под названием флагов визуализации, где геометрия может либо переопределить флаги рендеринга (установить), либо отключить флаги рендеринга (установить в 0) , или просто не влияют на флаги рендеринга (не заданы), необязательным будет ясный способ представить это.
Очевидно, что указатель на int в этом примере может достичь цели или, лучше, указателя общего доступа, поскольку он может предложить более чистую реализацию, однако я бы сказал, что в этом случае речь идет о ясности кода. Является ли нуль всегда «не установленным»? С указателем это не ясно, поскольку нуль буквально означает, что не выделено или создано, хотя оно может, но может необязательно означает «не установлено». Стоит отметить, что указатель должен быть выпущен, и в хорошей практике, установленном в 0, однако, как с общим указателем, необязательный не требует явной очистки, поэтому нет необходимости смешивать очистку с необязательно не установлен.
Я считаю, что речь идет о ясности кода. Ясность снижает стоимость обслуживания кода и разработки. Четкое понимание намерений кода невероятно ценно.
Использование указателя для представления этого потребует перегрузки концепции указателя. Чтобы представить «null» как «not set», обычно вы можете увидеть один или несколько комментариев с помощью кода, чтобы объяснить это намерение. Однако это не плохое решение, а не факультативное, но я всегда предпочитаю неявную реализацию, а не явные комментарии, поскольку комментарии не подлежат исполнению (например, путем компиляции). Примеры этих неявных элементов для разработки (те статьи в разработке, которые предоставляются исключительно для обеспечения намерения) включают различные стили стиля C++, «const» (особенно для функций-членов) и тип «bool», чтобы назвать несколько. Возможно, вам действительно не нужны эти функции кода, если все подчиняются намерениям или комментариям.
[boost.optional docs] (http://www.boost.org/doc/libs/1_53_0/libs/optional/doc/html/index.html#optional.motivation) может пролить некоторый свет. – juanchopanza
Кажется, что std :: unique_ptr может, как правило, обслуживать одни и те же варианты использования. Я предполагаю, что если у вас есть что-то против нового, то опционально может быть предпочтительным, но мне кажется, что (разработчики | приложения) с таким видом на новые находятся в небольшом меньшинстве ... AFAICT, необязательно, НЕ отличная идея. По крайней мере, большинство из нас могли спокойно жить без него. Лично я был бы более комфортным в мире, где мне не нужно выбирать между unique_ptr и опциональным. Назовите меня сумасшедшим, но Zen of Python прав: пусть будет один правильный способ что-то сделать! – allyourcode
Мы не всегда хотим выделить что-то в куче, которая должна быть удалена, поэтому unique_ptr не заменяет необязательный. – Krum