Значение &x
в этом контексте должно храниться где-то в скомпилированном коде, что приводит к другому поведению, чем если бы у вас была ссылка на объект, выделенный в стеке, верно?
Как ссылки работают под капотом, зависит от реализации. Каждый компилятор может заставить их работать так, как они хотят. Но обычно самым простым решением является обработка T&
как T * const
, который никогда не может быть nullptr
и чей адрес никогда не может быть получен. В тех случаях, когда упомянутый объект известен во время компиляции, тогда ссылку можно оптимизировать, и она, как никогда, не существовала. Но это также верно для указателей. Просто дополнительное свойство отсутствия адресации упрощает оптимизацию ссылок.
В чем вы можете быть уверены, так это в том, что поведение не будет отличаться в зависимости от того, как указанный объект был создан (динамически или как локальный объект). Да, случаи, которые вы показываете, могут привести к тому, что компилятор будет делать разные вещи, но поведение кода не является свойством того, что делает компилятор. Это рассуждение задом наперед. Код определяет строгое поведение в соответствии со стандартом языка. Все, что делает компилятор, должно оставить это поведение нетронутым, поскольку оно соблюдает то, что говорится в стандарте. Таким образом, ссылки имеют поведение, и некоторые варианты использования поведения будут инициировать создание разных сборок, но этот результат будет вести себя одинаково.
Обратите внимание, что ссылки имеют несколько дополнительных требований к указателям, что делает сравнение с указателями немного неточно. В качестве примера см. этот вопрос .
Но почему нельзя изменить адрес ссылки на выделенный объект кучи (поскольку это должно быть значение l до я понимаю, из-за невозможности узнать это во время компиляции)?
Причина, по которой вы не можете изменить то, на что ссылается ссылка, не является технической. По своей сути вы не хотите изменять то, к чему это относится, даже если бы эту функцию было легко реализовать. Это потому, что неизменный характер ссылок считается преимуществом. Если вам нужна ссылка, которая гарантированно содержит адрес и которую вы можете изменить для ссылки на другие вещи, язык уже содержит указатели, которые могут сделать это для вас.
Почему у нас есть только «ссылочный» тип, а не «ссылка на выделенный объект стека» + «ссылка на выделенные объекты кучи», поскольку в реализации они явно не обрабатываются одинаково?
Опять же, это не потому, что Реализация обрабатывает различные варианты использования функции, которая должна раскрывать интерфейс для разработчиков. Если вы пишете void mutate(int &)
функцию, вы хотите обрабатывать все int
с одинаковым образом, независимо от того, как они были созданы или какие оптимизации мог бы предложить компилятор. На самом деле не существует варианта использования функции, которая работает только с объектами, выделенными из стека.