Подтвердить что ты не робот

Являются ли функции возвратными значениями автоматическими объектами и, следовательно, гарантированно будут уничтожены?

В [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.

4b9b3361

Ответ 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 (...) {}
}

И результат:

В() А() ~ В

Так что да, это может быть ошибка.