Код в вопросе является неправильным и должен быть отклонен, но все же вопрос будет относиться к его небольшому изменению, но перед его обсуждением давайте опишем следующую более простую программу:
type f() {
type x; // [1]
return x; // [2]
}
int main() {
type m = f(); // [3]
m.const_function();
}
ВВ приведенной выше программе концептуально есть 3 объекта, x
внутри f
, возвращаемое значение без имени и, наконец, m
внутри main
.x
создается в [1], затем копируется в возвращенный объект в [2], который в конечном итоге используется для копирования конструкции m
в [3].
Теперь вернемся к эталонному случаю:
type f() {
type x; // [1]
return x; // [2]
}
int main() {
type const & r = f(); // [3]
r.const_function();
}
Концептуально происходит то же самое.Объект x
создается в [1] и копируется в возвращаемое значение в [2], теперь в [3] это возвращаемое значение используется не для инициализации r
, а для инициализации безымянного объекта, введенногокомпилятор.Наконец, в [3] постоянная ссылка связана с неназванным объектом.На этом этапе объект без имени находится в контексте main
.
Что касается того, что на самом деле происходит в текущих компиляторах, соглашение о вызовах определяет, как реализуется интерфейс функций.Соглашение о вызовах для функции, которая возвращает объект, который не помещается в регистры, реализуется (во всех известных мне компиляторах), передавая дополнительный скрытый указатель на функцию.То есть вызывающая сторона (в данном случае main
) резервирует место в стеке, а затем передает указатель на это местоположение вызываемой стороне.Это место, где вызывающая сторона и вызываемая сторона соглашаются, что возвращенный объект будет жить.Это означает, что вызывающая сторона может зарезервировать пространство для m
в исходном примере или для неназванного объекта во втором случае и передать указатель. Это удаляет один из объектов из уравнения как m
(или неназванный объект)и возвращенный объект одинаков.Следующий шаг выполняется компилятором при обработке вызываемого, если он может определить, что x
будет возвращено во всех путях кода, он не создаст x
в своем собственном стеке, но непосредственно в памяти, на которую ссылается скрытыйуказатель.Когда это будет сделано, чистый эффект состоит в том, что в программе будет один объект:
main
получит память и передаст указатель на f
, f
создастx
в этой области памяти (которая находится в main
стековом пространстве) и возврат к main
, в случае ссылки const
компилятор alias безымянным объектомимя ссылки.
(Примечание: ссылка и неназванный объект не одинаковы, их типы могут фактически отличаться, но ссылка будет псевдонимом для безымянного объекта, который предоставляет имя для него)