Сначала я отвечу на вопрос, а затем предоставлю некоторый контекст для ответа. Текущий рабочий черновик содержит следующую формулировку:
Временный объект, к которому привязана ссылка, или временный объект, являющийся полным объектом подобъекта, на который ссылаетсяпривязка сохраняется в течение всего срока действия ссылки, если значение, к которому привязана ссылка, было получено одним из следующих способов:
- временное преобразование материализации ([conv.rval]),
(
выражение )
, где выражение является одним из этих выражений, - подписка ([expr.sub]) операнда массива, где этот операнд является одним из этихвыражения,
- доступ к члену класса ([expr.ref]) с использованием оператора
.
, где левый операнд является одним из этих выражений, а правый операнд обозначает нестатический элемент данных не ссылочного типа, - операция указателя на член ([expr.mptr.oper]) с использованием оператора
.*
, где левый операнд является одним из этих выражений, аправый операнд - указатель на элемент данных не ссылочного типа, - a
const_cast
([expr.const.cast]), static_cast
([expr.static.cast]), dynamic_cast
([expr.dynamic.cast]) или reinterpret_cast
([expr.reinterpret.cast]) преобразование без определяемого пользователем преобразования операнда glvalue, который является одним из этих выражений, в значение glvalue, которое относится к обозначенному объектуоперандом или его полным объектом или подобъектом, - условное выражение ([expr.cond]), которое является glvalue, где второй или третий операнд является одним из этих выражений, или
- выражение запятой ([expr.comma]), которое является glvalue, где правый операнд является одним из этих выражений.
В соответствии с этим, когда ссылка связана сglvalue, возвращаемое из вызова функции, продление времени жизни не происходит, потому что glvalue было получено из вызова функции, который не является одним из разрешенных выражений для продления жизни.
Время жизни временного y+1
равнобывшийкак правило, один раз, когда связан с опорным параметром b
.Здесь значение y+1
материализуется для получения значения x, а ссылка привязывается к результату преобразования временной материализации;таким образом происходит продление жизни.Однако когда функция min
возвращает значение, ref2
привязывается к результату вызова, и продление срока службы здесь не происходит.Следовательно, временное значение y+1
уничтожается в конце определения ref2
, и ref2
становится висячей ссылкой.
В этой теме исторически существовала некоторая путаница.Хорошо известно, что код OP и подобный код приводят к висячей ссылке, но стандартный текст, даже на C ++ 17, не дает однозначного объяснения, почему.
Частоутверждал, что продление срока действия применяется только тогда, когда ссылка привязывается «напрямую» к временному, но стандарт никогда ничего не говорил об этом.Действительно, стандарт определяет, что он означает для ссылки «связывать напрямую», и это определение (, например, , const std::string& s = "foo";
является косвенным ссылочным связыванием) здесь явно не имеет значения.
Rakete1111 сказал в комментарии в другом месте SO, что продление времени жизни применяется только тогда, когда ссылка привязывается к prvalue (а не к некоторому glvalue, который был получен посредством предыдущей привязки ссылки к этому временному объекту);Похоже, они говорят что-то похожее здесь «связанными ... напрямую».Тем не менее, нет текстовой поддержки этой теории.В самом деле, код, подобный следующему, иногда рассматривается для запуска продления времени жизни:
struct S { int x; };
const int& r = S{42}.x;
Однако в C ++ 14 выражение S{42}.x
стало xvalue, поэтому, если здесь применяется продление времени жизни, то оноэто не потому, что ссылка привязана к prvalue.
Вместо этого можно утверждать, что продление срока действия применяется только один раз, а привязка любых других ссылок к тому же объекту не продлевает его время жизни.Это объясняет, почему код OP создает висячую ссылку, не предотвращая продление срока службы в случае S{42}.x
.Тем не менее, в стандарте также нет никаких заявлений об этом.
StoryTeller также сказал здесь, что ссылка должна связываться напрямую, но я также не знаю, что он подразумевает под этим.Он цитирует стандартный текст, указывающий, что привязка ссылки к временному в операторе return
не продлевает срок его службы.Однако это утверждение, похоже, предназначено для применения к случаю, когда рассматриваемый временный объект создается полным выражением в операторе return
, поскольку в нем говорится, что временный объект будет уничтожен в конце этого полного выражения.Очевидно, что это не относится к временному y+1
, который вместо этого будет уничтожен в конце полного выражения, содержащего вызов min
.Таким образом, я склонен думать, что это утверждение не было предназначено для применения в подобных случаях в этом вопросе.Вместо этого его эффект, наряду с другими ограничениями на продление срока службы, заключается в , препятствующем продлению срока жизни любого временного объекта за пределы области блока, в которой он был создан .Но это не помешает y+1
временным в вопросе выжить до конца main
.
Таким образом, остается вопрос: каков принцип, объясняющий, почему привязка ref2
к временномув вопросе не продлевает срок службы этого временного?
Формулировка из текущего рабочего проекта, который я цитировал ранее, была введена резолюцией CWG 1299 , которая была открыта в 2011 году, но толькорешен недавно (не вовремя для C ++ 17).В некотором смысле это проясняет интуицию о том, что ссылка должна связываться «напрямую», определяя те случаи, когда привязка является «прямой», достаточной для продления срока службы;однако, оно не настолько ограничительно, чтобы разрешать его только тогда, когда ссылка привязывается к prvalue.Это позволяет продлить срок службы в случае S{42}.x
.