Рассмотрим функции, которые должны возвращать значение, но которые могут не иметь значения для возврата:
- (А)<
doublesqrt(doublen);
>
- (В)<
charget_async_input();
>
- (С)<
pointpolygon::get_any_point_effectively_inside();
>
Существуют различные подходы к вопросу об отсутствии ценности для возвращения.
Типичный подход заключается в том, чтобы рассматривать существование действительного значения возврата как постусловие, так что, если функция не может вычислить значение для возврата, она имеет либо неопределенное поведение (и может использовать утверждение в сборке отладки), либо использует проверку времени выполнения и бросает исключение, если постусловие нарушено. Это разумный выбор, например, для функции (A), поскольку отсутствие надлежащего значения возврата напрямую связано с недействительным параметром (вне аргумента домена), поэтому уместно потребовать от абонента предоставить только параметры в действительном домене для нормального продолжения выполнения.
Однако функция (B) из-за своей асинхронной природы не терпит неудачу только потому, что не может найти значение для возврата; поэтому неправильно считать такую ситуацию ошибкой и утверждать или бросать исключение. Эта функция должна вернуться, и каким-то образом должна сказать получателю, что она не возвращает значимую ценность.
Аналогичная ситуация происходит с функцией (С): концептуально ошибкой является запрос.нулевая площадьполигон для возврата точки внутри себя, но во многих приложениях просто непрактично по причинам производительности рассматривать это как ошибку (потому что обнаружение того, что у полигона нет области, может быть слишком дорогим, чтобы его можно было проверить ранее), и либо возвращается произвольная точка (обычно в бесконечности), либо используется какой-то эффективный способ сказать вызывающему лицу, что такой точки нет.
Существуют различные механизмы, позволяющие функциям сообщать, что возвращенное значение недействительно. Один из таких механизмов, который довольно распространен, поскольку он имеет нулевые или незначительные накладные расходы, заключается в использовании специального значения, которое зарезервировано для передачи этого. Классическими примерами таких специальных значений являются<EOF
>,<string::npos
>, точки на бесконечности и т.д.
Когда эти значения существуют, т.е. возвратный тип может содержать все значимые значенияплюссигнал, этот механизм вполне уместен и хорошо известен. К сожалению, бывают случаи, когда таких ценностей не существует. В этих случаях обычной альтернативой является использование более широкого типа, такого как<int
>вместо<char
>; или сложного типа, такого как<std::pair<point,bool>
>.
Возвращение<std::pair<T,bool>
>, таким образом, прикрепление булевого флага к результату, который указывает, является ли результат значимым, имеет преимущество, которое может быть превращено в последовательную идиому, поскольку первым элементом пары может быть любой элемент, который концептуально возвращался бы. Например, последние две функции могут иметь следующий интерфейс:
std::pair<char,bool> get_async_input();
std::pair<point,bool> polygon::get_any_point_effectively_inside();
Эти функции используют согласованный интерфейс для работы с несуществующими результатами:
std::pair<point,bool> p = poly.get_any_point_effectively_inside();
if ( p.second )
flood_fill(p.first);
Однако это не только синтаксическое бремя, но и подвержено ошибкам, поскольку пользователь может легко использовать результат функции (первый элемент пары), не проверяя, имеет ли он действительное значение.
Очевидно, нам нужна лучшая идиома.