По моему мнению, функция должна генерировать исключение, если она не может сдержать свое «обещание», если она должна нарушить свой «контракт». Подпись функции (имя и параметры) определяет ее контракт.
Учитывая эти две функции-члена:
const Apple* FindApple(const wchar_t* name) const;
const Apple& GetApple(const wchar_t* name) const;
Имена этих функций, а также их возвращаемые значения указывают мне, что в случае FindApple функция вполне способна возвращать NULL, когда правильное яблоко не было найдено, но в случае GetApple вы ожидаете возвращения яблока. Если эта вторая функция не может сдержать свое обещание, она должна выдать исключение.
Исключения предназначены для тех исключительных условий, в которых у функции нет другого способа сообщить об этих условиях. Если вы решите сделать его частью обещания (читай: сигнатура функции), он сможет сообщить об этом условии, не вызывая исключения.
Обратите внимание, что в случае FindApple звонящий сам должен решить, как обработать условие "не найти правильное яблоко", поскольку оно больше не является исключительным условием.
У вас может возникнуть искушение попытаться избежать всех исключений, но это означает, что вы должны учитывать все возможные исключительные условия, и вместо этого вы возлагаете бремя на вызывающего абонента. Затем вызывающий абонент должен проверить «условия ошибки».
В конечном счете, исключение должно быть обработано, но только вызывающим абонентом, который знает, как обработать определенное условие полезным способом . И я имею в виду это в самой широкой возможной интерпретации: сервис, который отказывается, попробует еще раз позже, пользовательский интерфейс, который предоставляет полезное сообщение об ошибке, веб-приложение, которое отображает экран "упс", но хорошо восстанавливается, ... и так далее .
Dave