Поймать исключение по ссылке на констант

Это действительно как обработчик исключений, где 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.

Это означает, что объект исключения никогда не является "рожденным константой" и поэтому не должен вызывать изменение неопределенного поведения.

Я не вижу доказательств какой-либо "хитрости" в стандарте. Не могу доказать отрицание, но я верю, что ты "в безопасности". constNess, кажется, эквивалентен по форме этому:

T obj;
const T& t = obj;
const_cast<T &>(t).func();

Это constНесс сначала появляется на ссылке, которая существует внутри catch блок и это все.

Но все это действительно ставит вопрос: если вы не можете быть уверены в этом, зачем вообще это делать?

Просто поймать T&, конечно.

Как сказано здесь:

Если параметр предложения catch является ссылочным типом, любые внесенные в него изменения отражаются в объекте исключения и могут наблюдаться другим обработчиком, если исключение повторно вызывается с помощью throw;

Таким образом, это говорит о том, что можно безопасно изменить объект исключения, и изменения будут заметны.

Однако стоит подумать только о ловле по обычной ссылке. Или сделайте функцию const если он может работать только на участниках, помеченных как mutable,

Все это не звучит читабельно и может сделать со-разработчиков враждебными.

Другие вопросы по тегам