В соответствии со стандартом, это поведение является неправильным, и это уже упоминалось в разделе комментариев вопроса.
Это указано в разделе Обработка исключений .
В соответствии с сообщениями о дефектах на open-std.org они знали, что реализации (GCC и Clang) ошибались в этом уже в 2015-09-28 ,
Но предлагаемое решение было только в феврале 2016 года, и компиляторы (GCC и Clang) еще не включили это исправление.
Предлагаемое решение (февраль 2016 года):
Изменить пункт 18.2 [кроме объекта] следующим образом:
Деструктор вызывается для каждого автоматического объекта типа класса, созданного, но еще не уничтоженного, поскольку был введен блок try. Если во время уничтожения временных или локальных переменных для оператора return выдается исключение (9.6.3 [stmt.return]), также вызывается деструктор для возвращаемого объекта (если есть). Объекты уничтожаются в порядке, обратном завершению их строительства. [Пример:
struct A { };
struct Y { ~Y() noexcept(false) { throw 0; } };
A f() {
try {
A a;
Y y;
A b;
return {}; // #1
} catch (...) {
}
return {}; // #2
}
На # 1 создается возвращаемый объект типа A. Затем локальная переменная b уничтожается (9.6 [stmt.jump]). Затем локальная переменная y уничтожается, вызывая раскручивание стека, что приводит к уничтожению возвращаемого объекта и последующему уничтожению локальной переменной a. Наконец, возвращаемый объект снова создается в # 2. - конец примера]
Были обнаружены ошибки, связанные с этой проблемой, в GCC и Clang .
Комментарии к отчету об ошибках GCC указывают, что это явно ошибка.
Джонатан Уэйкли комментарии:
Сейчас 2013, поэтому разумно не возвращать по значению, если ваш деструктор может бросить.
И еще один пользователь:
Да, я заметил, и у Clang также была обнаружена ошибка, которая длилась годами. Тем не менее, поведение не так.