Являются ли функции возвратными значениями автоматическими объектами и, следовательно, гарантированно будут уничтожены?
В [except.ctor] стандарт (N4140) гарантирует, что:
... деструкторы вызываются для все автоматические объекты, созданные с блока try поступил...
Однако в следующем примере пустой output доказывает, что возвращаемое значение функции foo
не разрушено, хотя оно было построено, Скомпилирован с использованием g++ (5.2.1) и clang++ (3.6.2-1) и с опциями -O0 -fno-elide-constructors -std=c++14
.
struct A { ~A() { cout << "~A\n"; } };
struct B { ~B() noexcept(false) { throw 0; } };
A foo() {
B b;
return {};
}
int main() {
try { foo(); }
catch (...) { }
}
Является ли это ошибкой как в g++, так и в clang++, или являются возвращаемыми значениями функции
считаются автоматическими объектами, или это петлевое отверстие на языке С++?
Ни в одном из [stmt.return], [expr.call] или [dcl.fct] я не смог найти
ясное утверждение, является ли возвращаемое значение функции автоматическим
объект. Ближайшие намеки, которые я нашел, - 6.3.3 p2:
... Оператор возврата может включать строительство и копирование или перемещение временного объекта...
и 5.2.2 p10:
Вызов функции - это lvalue, если тип результата равен lvalue ссылочный тип или ссылочный номер rvalue для типа функции, значение x, если result type - это ссылка rvalue на тип объекта и в противном случае значение prvalue.
Ответы
Ответ 1
Возвращаемые значения функции считаются временными, а построение возвращаемого значения секвенируется до уничтожения локальных пользователей.
К сожалению, это не указано в стандарте. Существует открытый дефект, который описывает это, и предлагает некоторые формулировки, чтобы исправить проблему.
[...] Оператор return с операндом типа void должен использоваться только в функции, возвращаемый тип которой является vvoid. Оператор return с любым другим операндом должен использоваться только в функции, тип возврата которой не является vv void; оператор return инициализирует объект или ссылку, которая должна быть возвращена путем инициализации копирования (8.5 [dcl.init]) из операнда. [...]
Инициализация копии возвращаемого объекта секвенируется до уничтожения временных объектов в конце полного выражения, установленного операндом оператора return, который, в свою очередь, секвенирован до уничтожения локальных переменных (6.6 [stmt.jump]) блока, содержащего оператор return.
Так как возвращаемые значения функции являются временными, они не покрываются цитатой destructors are invoked for all automatic objects
в начале вашего сообщения. Однако [class.temporary]/3
говорит:
[...] Временные объекты уничтожаются как последний шаг при оценке полного выражения, которое (лексически) содержит точку, в которой они были созданы. Это верно, даже если эта оценка заканчивается выбросом исключения. [...]
Итак, я думаю, вы могли бы подумать об этом в GCC и Clang.
Не бросайте из деструкторов;)
Ответ 2
Это ошибка, и на этот раз MSVC действительно прав: она печатает "~ A".
Ответ 3
Я изменил ваш код, и я думаю, что теперь из вывода видно, что A не разрушен.
#include<iostream>
using namespace std;
struct A {
~A() { cout << "~A\n"; }
A() { cout << "A()"; }
};
struct B {
~B() noexcept( false ) { cout << "~B\n"; throw(0); }
B() { cout << "B()"; }
};
A foo() {
B b;
return;
}
int main() {
try { foo(); }
catch (...) {}
}
И результат:
В() А() ~ В
Так что да, это может быть ошибка.