При передаче временного объекта в функцию по константной ссылке какой срок жизни действует, если функция сохраняет эту ссылку для последующего использования?
Временный объект, переданный через параметр-ссылку, живёт до конца полного выражения, в котором выполнен вызов функции. Если функция сохраняет эту ссылку, после завершения полного выражения она становится висячей, даже если ссылка хранится в поле другого объекта.
Ссылка не является владельцем ресурса и не продлевает время жизни временного объекта при присваивании или сохранении.
Ссылки в C++ создавались как механизм обращения к существующим объектам без копирования и без отдельного владения их памятью. Чтобы ими было удобно пользоваться с временными значениями, язык предусматривает продление времени жизни временных объектов в некоторых контекстах.
Это правило уменьшает количество лишних копирований, но оно ограничено конкретным контекстом привязки. Оно не превращает ссылку в безопасный владеющий объект и не распространяется автоматически на все ссылки, которым затем присвоили исходную ссылку.
Функция может принять временный объект по константной ссылке и сохранить адрес или ссылку на него в поле, глобальной структуре либо объекте-кэше. На момент возврата из функции такой объект ещё может существовать, поэтому ошибка часто проявляется не сразу.
После завершения полного выражения временный объект уничтожается. Последующее обращение к сохранённой ссылке приводит к неопределённому поведению: данные могут выглядеть корректно, измениться или вызвать сбой.
Для параметра ссылочного типа временный объект обычно существует как минимум до завершения полного выражения с вызовом функции. Например, при отдельном вызове это означает, что объект сохраняется до точки с запятой после вызова, но не дольше.
Сохранение ссылки не создаёт нового продления времени жизни:
В примере временная строка уничтожается после завершения полного выражения с вызовом remember. Указатель saved после этого не становится нулевым: он просто указывает на уже уничтоженный объект.
Важно отличать параметры от локальных ссылок. В некоторых случаях привязка временного объекта непосредственно к локальной ссылке продлевает его жизнь до конца области действия этой ссылки, но передача такой ссылки дальше не переносит это продление на новую ссылку. Также нельзя рассчитывать на продление жизни объекта при возврате ссылки из функции.
Если получатель должен владеть значением дольше вызова, безопасные варианты — скопировать или переместить объект во внутреннее хранилище, принять его по значению, либо явно передать владельцу объект с подходящей семантикой. Если функция лишь использует объект во время вызова и ничего не сохраняет, параметр-ссылка может быть корректным решением.
Компромисс заключается в выборе между затратами копирования и безопасностью времени жизни. Ссылка эффективна для кратковременного невладеющего доступа, но требует явного контракта: вызывающий код обязан обеспечить существование объекта на весь период использования.
Компонент форматирования принимал текст по константной ссылке и сохранял ссылку для отложенного журналирования. Для строк, находившихся у вызывающего кода, это иногда работало, но при передаче временного результата конкатенации журналирование обращалось к уничтоженной строке.
Рассматривались три варианта:
const std::string& — нет лишних копирований, но сохраняется риск висячей ссылки;std::string — безопасное владение и предсказуемое время жизни, но возможна копия или перемещение;std::shared_ptr<const std::string> — удобно разделять владение, но появляются контрольный блок, дополнительные расходы и более сложная семантика владения.Выбрали хранение строки по значению: компонент действительно использовал данные после завершения вызова, поэтому ему требовалось собственное владение. После изменения контракта обращения к уничтоженным временным строкам исчезли, а стоимость копирования оказалась приемлемой для размера сообщений.
1. Продлевает ли присваивание ссылки другой ссылке время жизни временного объекта?
Нет. Продление связано с конкретным контекстом первоначальной привязки, а не с последующими ссылками. Если временный объект был передан в функцию через параметр, сохранённая внутри функции ссылка перестанет быть действительной после завершения полного выражения вызова.
2. Чем отличается срок жизни временного объекта от времени жизни параметра-ссылки?
Параметр-ссылка существует как параметр функции только во время её выполнения. Временный объект, связанный с этим параметром, обычно живёт дольше — до конца полного выражения, содержащего вызов, — но это всё равно не означает, что он живёт после вызова. Ссылка и объект имеют разные времена жизни.
3. Можно ли вернуть из функции константную ссылку на временный объект, созданный внутри этой функции?
Нет, это создаёт висячую ссылку. Локальный временный объект уничтожается при выходе из функции, поэтому возвращаемая ссылка обращается к объекту, чьё время жизни уже завершилось. Если результат должен пережить функцию, его следует вернуть по значению или передать владение подходящим объектом.