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;
}

Я думаю, что правило для ссылок на временные работает только для локальных переменных, а не членов.

Другие вопросы по тегам