Позднее уничтожение параметров функции

Согласно 5.2.2 / 4 «Вызов функции» в n4640 (8.2.2 / 4 в n4659) параметры функции создаются и уничтожаются в контексте вызывающей стороны. И реализации могут задерживать уничтожение параметров функции до конца включающего полного выражения (как особенность, определяемая реализацией). Обратите внимание, что выбор не неопределенные, скорее от реализации.

(Не совсем ясно, как это согласуется с 3.3.3 «Область видимости блока» (6.3.3 в n4659), из которой следует, что параметры функции имеют область видимости блока, а затем 3.7.3 «Длительность автоматического хранения» (6.7.3 в n4659), в котором говорится, что хранилище для переменных области видимости блока сохраняется до тех пор, пока не завершится блок, в котором они созданы. Но давайте предположим, что я что-то упускаю / неправильно понимаю в формулировке. Видимо теперь параметры функции будут иметь их собственная сфера)

Насколько я знаю, ABI требует, чтобы GCC и Clang задерживали уничтожение параметров функции до конца полного выражения, т.е. это поведение, определяемое реализацией этих компиляторов. Я бы предположил, что в подобных реализациях должно быть нормально возвращать ссылки / указатели на параметры функции, если эти ссылки / указатели используются только в вызывающем выражении.

Тем не менее, следующий пример segfaults в GCC и отлично работает в Clang

#include <iostream>
#include <string>

std::string &foo(std::string s)
{
return s;
}

int main()
{
std::cout << foo("Hello World!") << std::endl;
}

Оба компилятора выдают предупреждение о возврате ссылки на локальную переменную, что совершенно уместно здесь. Быстрая проверка сгенерированного кода показывает, что оба компилятора действительно задерживают уничтожение параметра до конца выражения. Тем не менее, GCC по-прежнему намеренно возвращает «нулевую ссылку» из foo, что вызывает сбой. Тем временем Clang ведет себя «как ожидалось», возвращая ссылку на свой параметр s, который выживает достаточно долго, чтобы произвести ожидаемый результат.

(GCC легко обмануть в этом случае, просто делая

std::string &foo(std::string s)
{
std::string *p = &s;
return *p;
}

который исправляет segfault под GCC.)

Оправдано ли поведение GCC в этом случае, если предположить, что оно гарантирует «позднее» уничтожение параметров? Я пропускаю какой-то другой отрывок в стандарте, в котором говорится, что возвращаемые ссылки на параметры функции всегда неопределены, даже если их время жизни увеличивается реализацией?

13

Решение

Насколько я знаю, ABI требует, чтобы GCC и Clang задерживали уничтожение параметров функции до конца полного выражения.

Вопрос сильно зависит от этого предположения. Посмотрим, правильно ли это. Проект Itanium C ++ ABI 3.1.1 Значения параметров говорит

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

ABI не определяет продолжительность жизни, так что давайте проверим стандартную версию C ++ N4659 [Basic.life]

1.2 … Время жизни объекта o типа T заканчивается, когда:

1.3 если T является типом класса с нетривиальным деструктором (15.4), начинается вызов деструктора, или …

1.4 память, занимаемая объектом, освобождается или повторно используется объектом, который не вложен в o ([intro.object]).

Стандарт C ++ говорит, что время жизни в этом случае заканчивается, когда вызывается деструктор. Таким образом, ABI действительно требует, чтобы время жизни параметра функции расширяло полное выражение вызова функции.

Предполагая, что требования реализации определены, я не вижу UB в примере программы, поэтому он должен был ожидать ожидаемого поведения в любой реализации, которая гарантирует соблюдение ABI Itanium C ++. GCC, кажется, нарушает это.

GCC документы заявить, что

Начиная с версии 3 GCC, компилятор GNU C ++ использует промышленный стандарт C ++ ABI, Itanium C ++ ABI.

Таким образом, продемонстрированное поведение можно считать ошибкой.

С другой стороны, неясно, является ли это следствие измененной формулировки [expr.call] преднамеренным. Следствием может считаться дефект.


… Это говорит о том, что хранение переменных области видимости продолжается до тех пор, пока не завершится блок, в котором они созданы.

В самом деле. Но [Expr.call]/ 4, что вы цитируете, говорит «Параметры функции созданный и уничтожен в контекст звонящего«. Таким образом, хранение длится до конца блока вызова функции. Кажется, нет никакого конфликта с продолжительностью хранения.


Обратите внимание, что стандартные ссылки C ++ указывают на сайт, который периодически генерируется из ток черновик и поэтому может отличаться от N4659, который я цитировал.

5

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

Начиная с 5.2.2 / 4 вызов функции [expr.call], мне кажется, GCC верен:

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

-2

Хорошо, мой плохой ответ на приведенный ниже ответ из прежнего стандарта до C ++ 14, чтение C ++ 17, мне кажется, что и GCC, и Clang верны:

От: N4659 8.2.2 / 4 Вызов функции [expr.call]

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

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