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 не обнаруживает никаких ошибок во время выполнения.

Что мне не хватает в ссылках?


person peoro    schedule 23.12.2010    source источник
comment
Когда вы передаете ссылку конструктору объекта, который принимает ссылку, вы потенциально передаете ссылку на время жизни созданного объекта. В таком случае ваша обязанность убедиться, что объект, на который ссылаются, живет дольше, чем вновь созданный объект.   -  person Martin York    schedule 24.12.2010


Ответы (5)


Мне кажется недействительным в соответствии с 12.2/4:

Есть два контекста, в которых временные объекты уничтожаются не в конце полного выражения. Первый контекст — когда выражение появляется как инициализатор для декларатора, определяющего объект. В этом контексте временное значение, содержащее результат выражения, должно сохраняться до завершения инициализации объекта.

Временный объект будет жить только до тех пор, пока s не будет полностью построен, а не до момента вызова use.

person icecrime    schedule 23.12.2010

Как указывают другие, стандарт С++ заставляет компилятор сохранять временное значение 0 только на время вызова конструктора. На практике gcc сохраняет временное значение на время работы функции main, что приводит к тому, что программа работает, как и ожидалось. По этой причине нет никаких предупреждений или ошибок во время выполнения.

Но это работает только случайно. Не полагайтесь на это поведение.

person doron    schedule 23.12.2010

Здесь следует отметить не const, а ссылку. Константа — это просто инструмент для статического анализа. Вы должны быть осторожны со ссылками, потому что они могут кусаться.

int& f()
{
    int i = 2;
    return i;
}

Иногда компилятор достаточно умен, чтобы предупредить вас о проблемах, которые могут возникнуть во время выполнения, но иногда это не так. В любом случае компилятор не должен предупреждать вас об этом.

person wilhelmtell    schedule 23.12.2010
comment
Компиляторы предупреждают о возврате временных файлов (по крайней мере, GCC и CLANG). В любом случае, разве Valgrind не должен сообщать об ошибке времени выполнения? Конечно, это зависит от многих вещей, я должен посмотреть на сгенерированный код, в любом случае мне трудно думать иначе... - person peoro; 24.12.2010
comment
@peoro Код, который я разместил, приводит к неопределенному поведению. UB означает, что вещи могут или не могут взорваться, сейчас или позже, так, как вы ожидаете, или так, как вы этого не ожидаете. Таким образом, valgrind может увидеть или не увидеть что-то не так в том конкретном прогоне, который у вас был. - person wilhelmtell; 24.12.2010

Вот еще одна настройка вашего кода, на которую жалуется даже 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, поэтому оно уничтожается, когда эта функция возвращается. Это похоже на возврат адреса локальной переменной.

В других ответах указано, почему компилятору разрешено это делать, мой код - просто иллюстрация.

person Jester    schedule 23.12.2010

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

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

person Mark Ransom    schedule 23.12.2010
comment
0 является буквальным значением true, но я думаю, что в этом контексте (параметр ссылки const l-value) создается временная переменная. Ваш код не должен вести себя иначе, чем оригинал. - person Ben Voigt; 24.12.2010
comment
Какая разница между буквальным и временным в этом случае? В любом случае, это работает также при передаче f() конструктору S. - person peoro; 24.12.2010
comment
@ Бен Фойгт, я знаю, что он не должен вести себя по-другому, но я подумал, что это может вызвать некоторое предупреждение от компилятора. Теперь, когда я думаю об этом еще немного, я понимаю, почему это может быть не так. - person Mark Ransom; 24.12.2010
comment
@peoro: разница в том, что литерал — это значение r, а временное — это значение l. - person Steve Jessop; 24.12.2010