C++: постоянная ссылка на временный
Есть несколько вопросов о времени жизни постоянной ссылки на SO, но я до сих пор не понимаю.
Этот фрагмент кода действителен?
struct S
{
const int &ref;
S( const int &x ) : ref(x) { }
};
int main( )
{
S s( 0 );
// ...
use( s.ref );
// ...
return 0;
}
Интуитивно я бы сказал нет, так как 0
должен истечь после выражения (S s(0);
) оценивается.
Однако и GCC, и CLANG компилируют его нормально, без предупреждений, и valgrind не обнаруживает никаких ошибок во время выполнения.
Что мне не хватает в ссылках?
5 ответов
Мне кажется недействительным в соответствии с 12.2/4:
Есть два контекста, в которых временные разрушения разрушаются в другой точке, чем конец фуллэкспрессии. Первый контекст - это когда выражение появляется как инициализатор для декларатора, определяющего объект. В этом контексте временное хранилище, которое содержит результат выражения, должно сохраняться до завершения инициализации объекта.
Временный доживает до s
полностью построен, не до точки, где use
называется.
Как указывают другие, стандарт C++ только заставляет компилятор сохранять 0
временно вокруг на время вызова конструктора. На практике gcc хранит временное хранилище на протяжении main
функция, которая приводит к запуску программы, как и ожидалось. По этой причине нет предупреждений или ошибок времени выполнения.
Но это работает только случайно. Не полагайтесь на это поведение.
Здесь следует отметить, что const
но ссылка. Const - это просто инструмент для статического анализа. Вы должны быть осторожны со ссылками, потому что они могут кусаться.
int& f()
{
int i = 2;
return i;
}
Иногда компилятор достаточно умен, чтобы предупредить вас о проблемах, которые могут появиться во время выполнения, но иногда это не так. В любом случае компилятор не должен предупреждать вас об этом.
Вот еще одна настройка вашего кода, на которую жалуется даже valgrind:
#include <iostream>
struct S
{
const int &ref;
S( const int &x ) : ref(x) { }
};
S* foo()
{
return new S(0);
}
int main( )
{
S* s = foo();
std::cout << s->ref << std::endl;
return 0;
}
Обычно это помещает временный в кадр стека foo
функция, поэтому она будет уничтожена, когда эта функция вернется. Это похоже на возврат адреса локальной переменной.
В других ответах указано, почему компилятору разрешено это делать, мой код является лишь иллюстрацией.
0
не временный, это буквальный. Попробуйте это небольшое изменение в вашей программе:
struct S
{
const int &ref;
S( const int &x ) : ref(x) { }
};
int f()
{
return 0;
}
int main( )
{
S s( f() );
// ...
use( s.ref );
// ...
return 0;
}
Я думаю, что правило для ссылок на временные работает только для локальных переменных, а не членов.