Каков порядок уничтожения аргументов функции?

Если некоторая функция f с параметрами p_1, ..., p_n типов T_1, ..., T_n соответственно вызывается с аргументами a_1, ..., a_n и ее тело выдает исключение, завершает или возвращает, то в каком порядке аргументы уничтожены и почему? Если возможно, дайте ссылку на стандарт.

EDIT: я действительно хотел спросить о параметрах функции, но, как сказал T.C. и Коломбо удалось прояснить мое замешательство, я оставляю этот вопрос об аргументах и ​​задаю новый отдельный вопрос о параметрах. См. комментарии к этому вопросу для различия.


person jotik    schedule 02.05.2016    source источник
comment
Я не знаю порядок, но я думаю, что ответ на второй вопрос, потому что так сказано в стандарте...   -  person zmbq    schedule 03.05.2016
comment
Я не думаю, что существует предопределенный порядок (такой же, как при вызове функции с несколькими параметрами), но я был бы рад увидеть ответ, цитирующий стандарт. Хороший вопрос, +1.   -  person vsoftco    schedule 03.05.2016
comment
@MikhailGenkin Я мог бы представить случай, когда более поздние параметры ссылаются на более ранние и где код будет вести себя по-разному в зависимости от порядка запуска деструкторов аргументов.   -  person templatetypedef    schedule 03.05.2016
comment
@zmbq Если стандарту есть что сказать по этому вопросу, мне интересно, почему они выбрали именно этот подход.   -  person jotik    schedule 03.05.2016
comment
wg21.link/cwg1880. Это кажется недоопределенным.   -  person T.C.    schedule 03.05.2016
comment
Я не думаю, что это определено (и, похоже, T.C. согласен!)   -  person Mats Petersson    schedule 03.05.2016
comment
@templatetypedef Это может быть связано с отсутствием опыта, но я не могу представить этот случай. После выполнения функции все параметры уничтожаются перед переходом к другим операциям.   -  person Mikhail Genkin    schedule 03.05.2016
comment
@Т.С. Объекты параметров != аргументы. Аргументы, если они временные, уничтожаются, как обычно.   -  person Columbo    schedule 03.05.2016
comment
@Columbo Хм, нет особого смысла говорить об уничтожении аргументов, поэтому я предположил, что OP говорит об объектах параметров. В конце концов, аргументы вовсе не обязательно должны быть объектами.   -  person T.C.    schedule 03.05.2016
comment
@Т.С. Гораздо меньше смысла вызывать аргументы параметров. И параметры также не обязательно должны быть объектами. И что было бы неправильно в рассуждениях о времени жизни аргументов в каком-то выражении?   -  person Columbo    schedule 03.05.2016
comment
@Колумбо Не совсем так. Я постоянно вижу, как люди их смешивают. И поскольку время жизни аргументов не обязательно связано с выходом из функции - например. void foo(std::string); std::string s; foo(s);   -  person T.C.    schedule 03.05.2016
comment
@Т.С. Извините, я не знаю точной терминологии. Я хотел спросить, есть ли f(T v, T2 v2) {}, то в каком порядке уничтожаются v и v2. Изменил свой вопрос соответственно.   -  person jotik    schedule 03.05.2016
comment
Ответ по ссылке, предоставленной TC: open -std.org/jtc1/sc22/wg21/docs/cwg_active.html#1880 — это не указано, и производитель компилятора может делать все, что считает задокументируйте это [в идеале вне исходного кода компилятора])   -  person Mats Petersson    schedule 03.05.2016
comment
@Columbo Пожалуйста, помогите мне понять это. Итак, аргумент — это то, что передается функции, а параметр — это переменная, доступная ВНУТРИ функции?   -  person jotik    schedule 03.05.2016
comment
@Колумбо Спасибо. Я надеюсь, что мое редактирование поможет убрать беспорядок, который я сделал.   -  person jotik    schedule 03.05.2016
comment
@Columbo Я не думаю, что когда-либо встречал кого-то, кто так педантично относился к различию. Большинство людей, которых я знаю, используют параметр и аргумент взаимозаменяемо: main имеет argv и argc. не paramv и paramc.   -  person Rob K    schedule 03.05.2016
comment
Это различие иногда называют формальным аргументом (переменная, которая фиксирует то, что передается) и фактическим или эффективным аргументом (переданное значение).   -  person Jean-Baptiste Yunès    schedule 13.05.2016
comment
@RobK Это потому, что параметры main обозначают переданные аргументы (являющиеся указателем и скаляром, описывающим свойство указателя второго аргумента). И делать акцент на каком-то историческом именовании параметров функций довольно неубедительно. В любом случае, базовая терминология не слишком велика, не так ли, особенно при постановке вопроса, значение которого значительно меняется, когда эти термины меняются местами?   -  person Columbo    schedule 16.05.2016
comment
@RobK Возможно, люди, которых вы знаете, не меняют их произвольно, а говорят об аргументах и ​​параметрах, когда они на самом деле имеют в виду их, соответственно?   -  person Columbo    schedule 16.05.2016
comment
См. также: В чем разница между аргументом и параметром? Однако кажется, что обычно люди думают о параметрах как о вещах, определенных функция в качестве входных данных, тогда как аргументы — это то, что вы передаете в качестве параметров.   -  person jotik    schedule 16.05.2016


Ответы (3)


Порядок, в котором оцениваются аргументы функции, стандартом не определен. Из стандарта C++11 (онлайн-черновик):

5.2.2 Вызов функции

8 [ Примечание: Вычисления постфиксного выражения и выражений-аргументов не упорядочены друг относительно друга. Все побочные эффекты вычислений выражений аргументов упорядочиваются до входа в функцию (см. 1.9). —конец примечания ]

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

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

person R Sahu    schedule 02.05.2016
comment
Под зависимой от платформы вы имеете в виду реализацию, определенную? - person jotik; 03.05.2016
comment
@jotik, да. зависит от платформы - это разговорный термин :) - person R Sahu; 03.05.2016
comment
@RSahu Но, например, GCC, компилятор, работающий на многих платформах, может иметь одинаковое определяемое реализацией поведение как в Linux, так и в Windows, и то же самое может быть для Clang. Спецификация использует термин определенная реализация для обозначения того, что разработчикам решать, что здесь происходит; это имеет очень мало общего с платформой как таковой. - person cat; 03.05.2016
comment
@cat, мы тут мудрим. - person R Sahu; 03.05.2016
comment
Я думаю, что правильный термин на самом деле не указан. В ISO действительно есть реализация, определенная как категория, но это более строгое требование: оно буквально означает, что реализация должна публично определить, какой выбор они сделали. Unspecified означает, что выбор может варьироваться от выпуска к выпуску или даже зависеть от настроек компилятора. - person MSalters; 03.05.2016
comment
@MSalters, согласен. Полезно знать тонкую, но важную разницу между неуказанным поведением и поведением, определенным реализацией. - person R Sahu; 03.05.2016

Мне не удалось найти ответ в стандарте, но я смог протестировать его на трех самых популярных компиляторах, совместимых с C++. Ответ R Sahu в значительной степени объясняет, что это определяется реализацией.

§5.2.2/8: вычисления постфиксного выражения и все аргументы не упорядочены друг относительно друга. Все побочные эффекты оценки аргументов упорядочиваются до входа в функцию.

Компилятор Visual Studio C++ (Windows) и gcc (Debian)
Аргументы создаются в порядке, обратном их объявлению, и уничтожаются в обратном порядке (таким образом, уничтожаются в порядке описания):

2
1
-1
-2

Clang (FreeBSD)
Аргументы создаются в порядке их объявления и уничтожаются в обратном порядке:

1
2
-2
-1

Всем компиляторам было приказано обрабатывать исходный код как C++11, и я использовал следующий фрагмент, чтобы продемонстрировать ситуацию:

struct A
{
    A(int) { std::cout << "1" << std::endl; }
    ~A() { std::cout << "-1" << std::endl; }
};

struct B
{
    B(double) { std::cout << "2" << std::endl; }
    ~B() { std::cout << "-2" << std::endl; }
};

void f(A, B) { }

int main()
{
    f(4, 5.);
}
person Zereges    schedule 02.05.2016

В §5.2.2[4] N3337 довольно подробно описывает, что происходит (онлайн-черновик):

Во время инициализации параметра реализация может избежать построения дополнительных временных параметров, комбинируя преобразования связанного аргумента и/или создание временных параметров с инициализацией параметра (см. 12.2). Время жизни параметра заканчивается, когда возвращается функция, в которой он определен.

Так например в

f(g(h()));

возвращаемое значение из вызова h() является временным, которое будет уничтожено в конце полного выражения. Однако компилятору разрешено избегать этого временного и напрямую инициализировать своим значением параметр g(). В этом случае возвращаемое значение будет уничтожено после возврата g() (т.е. ПЕРЕД вызовом f()).

Если я правильно понял, что указано в стандарте, однако не разрешается, чтобы значение, возвращаемое из h(), сохранялось до конца полного выражения, если только не сделана копия (параметр), и эта копия уничтожается после возврата g().

Два сценария:

  1. Возвращаемое значение h используется для прямой инициализации параметра g. Этот объект уничтожается при возврате g и перед вызовом f.
  2. h возвращаемое значение является временным. Копия создается для инициализации параметра g и уничтожается при возврате g. Вместо этого исходное временное значение уничтожается в конце полного выражения.

Я не знаю, следуют ли реализации этим правилам.

person 6502    schedule 02.05.2016
comment
Сами объекты параметров не являются временными. - person T.C.; 03.05.2016
comment
Т.К. они являются временными в контексте вызывающего объекта и именованными объектами в контексте вызываемого объекта. - person 6502; 03.05.2016
comment
Разве компилятору не разрешено избегать создания временных объектов для вызовов функций? - person jotik; 03.05.2016
comment
@jotik: Да, но только в правилах «как если» — другими словами, только тогда, когда компилятор может ясно видеть, что общее поведение одинаково как для конструкции, так и для исключенной конструкции. Если есть сомнения, вызывается конструктор. - person Mats Petersson; 03.05.2016
comment
они являются временными в контексте вызывающего абонента {{необходима цитата}} - person T.C.; 03.05.2016
comment
@TC: я добавил ссылку на n3337, где проблема обсуждается более подробно. - person 6502; 03.05.2016
comment
@MatsPetersson: правила «как если бы» никогда ничего не покупают. Если вы можете сказать, было ли оно применено или нет, то оно не может быть применено. Легче рассуждать, вообще не задумываясь об этом... на самом деле это не имеет смысла. - person 6502; 03.05.2016
comment
Единственными параметрами в этом примере являются ссылки. - person T.C.; 03.05.2016
comment
@T.C.: почему ты думаешь, что это имеет значение? Даже если они были объектами, они существуют концептуально до входа в тело функции: функция получает их извне, вызывающая сторона отвечает за создание и уничтожение... для функции это просто обычные уже существующие именованные объекты. - person 6502; 03.05.2016
comment
@ 6502: Хотя стандарт может не определять средства, с помощью которых код может определить, когда применяется правило «как если бы», он не запрещает реализациям раскрывать свою внутреннюю работу способами, которые могут выявить такие вещи. Правило «как если бы» ясно дает понять, что компиляторы не обязаны гарантировать, что программы, исследующие внутреннюю работу сгенерированного кода, не увидят ничего, что отличалось бы от прямого перевода. - person supercat; 03.05.2016
comment
@supercat: если вы хотите поговорить о правиле «как если бы» в конкретной реализации, вы свободны. Однако в контексте стандартного языка C++ правило "как если бы" пусто, потому что его действие никаким образом не наблюдается в стандартном C++ по определению. Это как обсуждать невидимых фей в учебнике по физике. На мой взгляд, было бы лучше исключить это логически пустое правило из стандарта, поскольку оно не принадлежит этому миру и просто добавляет путаницы. ИМО имеет смысл в главе о генерации машинного кода, скажем, документации g++, а не в стандарте. - person 6502; 03.05.2016
comment
@ 6502 Извините за путаницу, которую я вызвал, ошибочно приняв аргументы за параметры. Поскольку я спросил об аргументах, я думаю, что в настоящее время вычеркнутая часть вашего ответа в настоящее время лучше всего отвечает на него. Смотрите мое редактирование вопроса и комментарии под вопросом. Я думаю, что аргументы лучше всего рассматривать как нечто, назначаемое параметрам перед вызовом функции. Так что говорить об аргументах в контексте их уничтожения в каком-то выражении вызова функции имеет смысл только в том случае, если компилятор решит использовать временные. В противном случае это случай уничтожения параметров и CWG#1880. - person jotik; 03.05.2016
comment
@jotik: к сожалению, нечеткость C ++ в отношении проблемы все равно присутствует. Проблема в том, что временные объекты должны уничтожаться в конце полного выражения, а параметры могут уничтожаться в конце вызова (!). Теоретически это означает, что компилятор C++, в котором вызываемый объект уничтожает параметры, должен всегда делать копию из временного аргумента в параметр, чтобы вызываемый объект мог уничтожить копию, но временный объект ожидает окончания функции. Я не думаю, что какой-либо автор компилятора сделал бы это, чтобы соблюсти абсурдные правила. Я думаю (надеюсь). - person 6502; 03.05.2016
comment
Это даже не обращается к вопросу, -1. - person Barry; 10.05.2016
comment
@Барри: Прости? аргументы могут быть временными (уничтожаются в конце полного выражения в порядке, обратном созданию), но также могут быть встроены непосредственно в параметры, которые уничтожаются при возврате из вызова (что может произойти до конца полного выражения). Почему вы думаете, что это не ответ на вопрос? - person 6502; 10.05.2016