Как использовать std :: необязательный?

Я читаю документацию std::experimental::optional и у меня есть хорошее представление о том, что он делает, но я не понимаю, когда Я должен использовать это или как я должен использовать это. На сайте пока нет примеров, что затрудняет мне понимание истинной концепции этого объекта. Когда std::optional хороший выбор для использования, и как он компенсирует то, что не было найдено в предыдущем стандарте (C ++ 11).

108

Решение

Самый простой пример, который я могу придумать:

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 (или нет)!

Это также ликвидирует проклятый «Проходить -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 документация. boost::optional а также std::optional будет в основном идентичным с точки зрения поведения и использования.

152

Другие решения

Пример цитируется из Новая принятая бумага: N3672, стандартная документация :: опционально:

 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
}
}
}
31

но я не понимаю, когда я должен использовать это или как я должен использовать это.

Подумайте, когда вы пишете 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 ]
}
}

Отредактируйте: относительно остальных ваших вопросов:

Когда STD :: необязательно, хороший выбор для использования

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

  • Когда вы хотите убедиться, что код клиента имеет проверить выходное значение (тот, кто пишет код клиента, может не проверять наличие ошибок — если вы попытаетесь использовать неинициализированный указатель, вы получите дамп ядра; если вы попытаетесь использовать неинициализированный std :: необязательный, вы получите ловимое исключение).

[…] и как это компенсирует то, что не было найдено в предыдущем стандарте (C ++ 11).

До C ++ 11 вам приходилось использовать другой интерфейс для «функций, которые не могут возвращать значение» — либо возвращать по указателю и проверять на NULL, либо принимать выходной параметр и возвращать код ошибки / результата для «недоступно» ».

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

std::optional хорошо заботится о проблемах, возникающих с предыдущими решениями.

9

Я часто использую необязательные параметры для представления необязательных данных, извлекаемых из файлов конфигурации, то есть, где эти данные (например, с ожидаемым, но не обязательным элементом в документе XML) предоставляются необязательно, так что я могу явно и ясно показать, если данные фактически присутствовали в документе XML. Особенно, когда данные могут иметь состояние «не установлено», а не «пусто» и «установлено» (нечеткая логика). При необязательном параметре set и not set значение clear также будет пустым со значением 0 или null.

Это может показать, что значение «не установлено» не эквивалентно «пустому». В принципе, указатель на int (int * p) может показать это, где нулевое значение (p == 0) не установлено, значение 0 (* p == 0) установлено и пусто, а любое другое значение (*п <> 0) устанавливается в значение.

Для практического примера у меня есть фрагмент геометрии, извлеченный из документа XML, который имеет значение, называемое флагами рендеринга, где геометрия может либо переопределить флаги рендеринга (установить), либо отключить флаги рендеринга (установить в 0), либо просто нет влияют на флаги рендеринга (не установлены), необязательный способ представить это.

Ясно, что указатель на int в этом примере может достигнуть цели или, что лучше, указателя общего ресурса, поскольку он может предложить более чистую реализацию, однако я бы сказал, что в данном случае речь идет о ясности кода. Нуль всегда «не установлен»? С указателем это не ясно, поскольку нуль буквально означает не выделенный или созданный, хотя это мог, еще может не обязательно значит «не установлено». Стоит отметить, что указатель должен быть освобожден, и в хорошей практике установить значение 0, однако, как и в случае с указателем общего доступа, необязательный параметр не требует явной очистки, поэтому нет необходимости смешивать очистку с необязательный не был установлен.

Я верю, что речь идет о ясности кода. Ясность снижает стоимость обслуживания кода и разработки. Четкое понимание намерения кода невероятно ценно.

Использование указателя для представления этого потребовало бы перегрузки концепции указателя. Чтобы представить «null» как «not set», обычно вы можете увидеть один или несколько комментариев через код для объяснения этого намерения. Это не плохое решение вместо необязательного, однако, я всегда выбираю неявную реализацию, а не явные комментарии, так как комментарии не подлежат исполнению (например, компиляцией). Примеры этих неявных элементов для разработки (те статьи в разработке, которые предоставляются исключительно для принудительного применения намерения) включают в себя различные приведения в стиле C ++, «const» (особенно для функций-членов) и тип «bool», если назвать несколько. Возможно, вам не нужны эти функции кода, если все подчиняются намерениям или комментариям.

4