Последовательность разрушения параметров функции

Согласно C ++ 14 [expr.call] / 4:

Время жизни параметра заканчивается, когда возвращается функция, в которой он определен.

Кажется, это подразумевает, что деструктор параметра должен быть запущен до того, как код, вызвавший функцию, продолжит использовать возвращаемое значение функции.

Тем не менее, этот код показывает по-другому:

#include <iostream>

struct G
{
G(int): moved(0) { std::cout << "G(int)\n"; }
G(G&&): moved(1) { std::cout << "G(G&&)\n"; }
~G() { std::cout << (moved ? "~G(G&&)\n" : "~G()\n"); }

int moved;
};

struct F
{
F(int) { std::cout << "F(int)\n"; }
~F() { std::cout << "~F()\n"; }
};

int func(G gparm)
{
std::cout << "---- In func.\n";
return 0;
}int main()
{
F v { func(0) };
std::cout << "---- End of main.\n";
return 0;
}

Выход для gcc и clang, с -fno-elide-constructors, (с моими аннотациями):

G(int)               // Temporary used to copy-initialize gparm
G(G&&)               // gparm
---- In func.
F(int)               // v
~G(G&&)              // gparm
~G()                 // Temporary used to copy-initialize gparm
---- End of main.
~F()                 // v

Итак, ясно vконструктор работает раньше gparmдеструктор. Но в MSVC, gparm уничтожен раньше vконструктор работает.

Та же самая проблема может быть замечена с включенным разрешением копирования и / или с func({0}) так что параметр имеет прямую инициализацию. v всегда строится раньше gparm разрушен. Я также наблюдал проблему в более длинной цепочке, например F v = f(g(h(i(j()))); не уничтожил ни один из параметров f,g,h,i до после v был инициализирован.

Это может быть проблемой на практике, например, если ~G разблокирует ресурс и F() приобретает ресурс, это будет тупик. Или если ~G бросает, тогда выполнение должно перейти к обработчику catch без v будучи инициализированным.

У меня вопрос: разрешает ли стандарт оба этих заказа? , Есть ли более конкретное определение отношения секвенирования, связанное с уничтожением параметров, чем просто та цитата из expr.call/4, в которой не используются стандартные термины секвенирования?

16

Решение

На самом деле я могу ответить на свой собственный вопрос … не нашел ответа во время поиска, прежде чем писать его, но потом снова поискал потом нашел ответ (типично да).

Во всяком случае: эта проблема CWG # 1880 с разрешением:

Примечания от встречи в июне 2014 года:

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

Последний мой набросок C ++ 17 (N4606) изменил текст в [expr.call] / 4:

Это зависит от реализации, заканчивается ли время жизни параметра, когда возвращается функция, в которой он определен, или в конце включающего полного выражения.

Я полагаю, что мы должны рассматривать эту резолюцию (то есть «определяемую реализацией») как применяемую задним числом, поскольку она не была четко определена опубликованными стандартами.

Примечание: определение полное выражение можно найти в C ++ 14 [intro.execution] / 10:

Полное выражение — это выражение, которое не является подвыражением другого выражения. […] Если языковая конструкция определена для создания неявного вызова функции, использование языковой конструкции считается выражением для целей этого определения.

Так F v { func(0) }; это вложение полное выражение за gparm (хотя это декларация, а не выражение!).

12

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]