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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

Ответ 2

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

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

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

2
1
-1
-2

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

1
2
-2
-1

Все компиляторы получили указание обработать исходный код как С++ 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.);
}

Ответ 3

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

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

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

f(g(h()));

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

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

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

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

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