Поймать исключение по ссылке на констант
Это действительно как обработчик исключений, где T
некоторый класс с неconst
функция-член func
?
Другими словами: это catch
гарантированно привязывается напрямую к (модифицируемому) объекту исключения, или компилятор может использовать некоторую хитрость, когда вы ловите по константной ссылке?
catch(const T &t)
{
const_cast<T &>(t).func();
}
4 ответа
Из [кроме. Броска]:
Оценка выражения броска с операндом вызывает исключение (15.1); тип объекта исключения определяется путем удаления любых cv-квалификаторов верхнего уровня из статического типа операнда и изменения типа "массив из T" или "функция, возвращающая T" в "указатель на T" или "указатель на функция, возвращающая T ", соответственно.
и, акцент мой:
Создание исключительной копии инициализирует (8.5, 12.8) временный объект, называемый объектом исключения. Временное значение является lvalue и используется для инициализации переменной, объявленной в соответствующем обработчике (15.3).
Поэтому, если мы выбрасываем операнд типа cv T, мы инициализируем копию временного объекта типа T.
Затем в соответствии с [exception.handle] обработчик для const T& (для типа без указателя T) сопоставляется для объекта исключения типа E, если:
- [...] E и T имеют одинаковый тип (игнорируя cv-квалификаторы верхнего уровня),
- [...] T является однозначным публичным базовым классом E
Этот обработчик инициализируется:
Переменная, объявленная объявлением исключения, типа cv T или cv T &, инициализируется из объекта исключения типа E следующим образом:
- если T является базовым классом E, переменная инициализируется копией (8.5) из соответствующего подобъекта базового класса объекта исключения;
- в противном случае переменная инициализируется копией (8.5) из объекта исключения.
Так что, если мы перехватываем с помощью const T&, мы инициализируем копию ссылки из объекта исключения - который, как мы знаем из предыдущего раздела, будет либо типа T, либо публично получен из T. Из [dcl.init.ref]:
Ссылка на тип "cv1 T1" инициализируется выражением типа "cv2 T2" следующим образом:
- Если ссылка является ссылкой lvalue и выражением инициализатора
- является lvalue (но не является битовым полем), а "cv1 T1" совместим со ссылками с "cv2 T2", или [...]тогда ссылка связана с инициализатором выражения lvalue в первом случае
Ключ в том, что объект временного исключения все еще является lvalue. Таким образом, если наш обработчик соответствовал для const T&, мы знаем, что ссылка связана непосредственно с объектом типа T или D (где D наследуется от T) - в любом случае, это тип, который является ссылочно-совместимым с const T
, Таким образом, не существует неопределенного поведения. Если временным объектом является значение rvalue или обработчик может соответствовать более широкому диапазону типов, то будет создан временный объект типа const T
- и ваш const_cast
определенно будет неопределенным поведением.
Хотя ваш код не проявляет неопределенного поведения на соответствующем компиляторе, на самом деле нет причин не делать этого:
catch(T &t)
{
t.func();
}
(Я использую C++11; мне нужно обновить;))
15.1 / 3 (выделено мое):
Выражение throw инициализирует временный объект, называемый объектом исключения, тип которого определяется путем удаления любых cv-квалификаторов верхнего уровня из статического типа операнда throw.
Это означает, что объект исключения никогда не является "рожденным константой" и поэтому не должен вызывать изменение неопределенного поведения.
Я не вижу доказательств какой-либо "хитрости" в стандарте. Не могу доказать отрицание, но я верю, что ты "в безопасности". const
Ness, кажется, эквивалентен по форме этому:
T obj;
const T& t = obj;
const_cast<T &>(t).func();
Это const
Несс сначала появляется на ссылке, которая существует внутри catch
блок и это все.
Но все это действительно ставит вопрос: если вы не можете быть уверены в этом, зачем вообще это делать?
Просто поймать T&
, конечно.
Как сказано здесь:
Если параметр предложения catch является ссылочным типом, любые внесенные в него изменения отражаются в объекте исключения и могут наблюдаться другим обработчиком, если исключение повторно вызывается с помощью throw;
Таким образом, это говорит о том, что можно безопасно изменить объект исключения, и изменения будут заметны.
Однако стоит подумать только о ловле по обычной ссылке. Или сделайте функцию const
если он может работать только на участниках, помеченных как mutable
,
Все это не звучит читабельно и может сделать со-разработчиков враждебными.