Согласно 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 оправданным в этом случае в предположении, что оно гарантирует "позднюю" разрушение параметров? Я пропустил какой-то другой отрывок в стандарте, в котором говорится, что возврат ссылок на функциональные параметры всегда undefined, даже если их время жизни расширяется реализацией?