#c #reference #try-catch
#c #ссылка #try-catch
Вопрос:
У меня есть некоторый код, который выглядит следующим образом:
const SomeType amp; elt = x.find();
try {
// ... do something with elt ...
} catch (...) {
// handle processing exception
}
И это работает, но из-за меняющихся обстоятельств теперь этот find()
метод может выдавать исключение «не найдено», которое мне нужно перехватить.
То, что я хотел бы написать, было бы
try {
const SomeType amp; elt = x.find();
} catch (...) {
// handle "not found" exception
return;
}
try {
// ... do something with elt ...
} catch (...) {
// handle processing exception
}
Но, конечно, это не работает, потому elt
что к моменту достижения его блока обработки он уже не находится в области видимости. Я не могу изменить это на
const SomeType amp; e<
try {
elt = x.find();
} catch (...) {
// handle "not found" exception
return;
}
потому что, конечно, ссылочные типы в C не могут быть неинициализированы. Итак, у меня остались варианты временной настройки elt
в качестве ссылки на фиктивный объект типа SomeType
, чего я бы предпочел не делать, или вложения блоков try / catch, например:
try {
const SomeType amp; elt = x.find();
try {
// ... do something with elt ...
} catch (...) {
// handle processing exception
}
} catch (...) {
// handle "not found" exception
}
Мне это тоже не нравится: вложенность сбивает с толку, и мне не нравится, как обработчик исключений «не найден» скрывается там в конце.
Кто-нибудь может придумать лучший способ организовать это? (В C, конечно, мы бы просто попросили find
функцию возвращать нулевой указатель в случае, если он не найден, и обрабатывали бы его таким образом, но мне нравится стараться не быть старым программистом на C, когда я пишу C , и в любом случае x.find()
он уже настроен на возврат ссылки,не указатель.)
Комментарии:
1. И именно поэтому я ненавижу функции поиска, которые завершаются через исключение, когда не найдены. Если бы вместо этого функция вернула a
std::pair<bool, pointer_to_object>
, вы могли бы зафиксировать это с помощью структурированной привязки, и тогда ваша проверка not found была бы оператором if сразу после вызоваfind
. Есть ли у вас какой-либо контроль над интерфейсомfind
?2. Поскольку логически невозможно » // … сделайте что-нибудь с elt …» если
find()
генерируется исключение, какой бы способ вы ни хотели обработать это исключение, он должен логически отличаться от того, что делает ваш существующий try / catch , прежде чем возобновить работу с остальной частью кода, который использует ссылку. Поэтому вам просто нужен другой блок try / catch вокруг этого , чтобы выполнить любую очистку, которую необходимо выполнить, еслиfind()
возникает исключение.3. @NathanOliver Они мне тоже не нравятся. У меня есть контроль над остальной частью кода, хотя я не уверен, что решу использовать его в этом случае.
Ответ №1:
Если исключения разные, вы можете использовать один и тот же блок try:
try {
const SomeTypeamp; elt = x.find();
// ... do something with elt ...
} catch (const NotFoundExceptionamp;) {
// handle "not found" exception
} catch (...) {
// handle processing exception
}
в противном случае повторная привязка ссылки не допускается, но может быть смоделирована указателем или std::reference_wrapper
вообще,
И необязательная ссылка (не разрешена ни в std, но boost разрешает ее) может быть смоделирована указателем или std::optional<std::reference_wrapper<T>>
.
Итак:
const SomeType* eltPtr = nullptr;
try {
eltPtr = amp;x.find();
} catch (const NotFoundExceptionamp;) {
// handle "not found" exception
return;
}
const SomeTypeamp; elt = *eltPtr;
try {
// ... do something with elt ...
} catch (...) {
// handle processing exception
}
Комментарии:
1. ДА. Я думаю, что первая часть вашего ответа — правильное решение. Я не могу использовать его напрямую (потому что, увы, исключения не отличаются), но они должны быть.
2. Решение с указателем, на которое я уже ссылался в вопросе, и отброшено.
3. @SteveSummit » увы, исключения не отличаются » — тогда как бы вы различали их для начала? Есть ли у них какие-либо данные-члены , которые отличаются в зависимости от обстоятельств?
4. @SteveSummit: владение указателями плохо, указатели, не являющиеся владельцами, в порядке (не обязательно лучший способ)
5. @RemyLebeau Поскольку исключения структурированы так, как они есть в настоящее время, единственный способ отличить их — это отдельные блоки try / catch, один из которых охватывает код поиска, а второй — обработку.
Ответ №2:
Разделить на другую функцию:
void func1()
{
try {
const SomeType amp; elt = x.find();
func2(elt);
} catch (...) {
// handle "not found" exception
}
}
void funct2(const SomeType amp; elt)
{
try {
// ... do something with elt ...
} catch (...) {
// handle processing exception
}
}
Хотя в целом я нахожу ваш интерфейс немного тревожным, поскольку в первую очередь требуются все эти блоки try / catch. К сожалению, трудно дать совет о том, как улучшить общий стиль с такой небольшой информацией.
Комментарии:
1. Это хорошая идея. Я не думаю, что буду его использовать (для второй функции нет подходящего названия, а поток управления оказывается таким же запутанным, как вложенные блоки try / catch), но вы правы, в целом это классический метод уменьшения нежелательной вложенности путем выделения чего-либо в функцию.
2. @SteveSummit Почему обработка исключительных материалов не обрабатывается деструкторами?
3. Я думаю, что ответ на ваш вопрос таков: «Потому что ни я, ни эта устаревшая кодовая база на самом деле не обернули наши головы вокруг RAII».
Ответ №3:
Это будет не всем по вкусу (поведение определяется кстати), но вы могли бы использовать
#include <functional>
std::reference_wrapper<const SomeType> elt = e<
try {
elt = x.find();
} catch (...) {
// handle "not found" exception
return;
}
Возможно, параметр elt
to сам по себе является злоупотреблением std::reference_wrapper
, конструкторы по умолчанию и перемещения удалены по замыслу: я обхожу это.
По сути std::refernce_wrapper
, это указатель под капотом, но вы можете его повторно привязать, что, по сути, и есть то, что вы хотите сделать здесь.
Комментарии:
1. Я бы никогда не подумал об этом! Я, конечно, согласен с тем, что это «не всем по вкусу» (действительно, в этом случае я бы сказал, что это лекарство хуже, чем болезнь), но я рад узнать о технике, поэтому спасибо за публикацию.
2. Вы уверены, что самоинициализация допустима? (элемент должен быть скопирован, поэтому считывание неинициализированного значения -> UB, как
int n = n;
).