Итак, отличия таки нашлись.
Ну, во-первых:
если у нас foo() внезапно начнет возвращать не переменную типа O, а таки O& (решили соптимизировать - возвращаем некую долгоживующую переменную, нет смысла создавать локальную копию её), то все внезапно поменяется:
Разумеется. Присваивание указателя переменной указательного типа не вызывает лишних копирований.
Это первое семантическое отличие. А вот второе:
Небольшая магия, скрывающая отсутствие необходимости вызывать конструктор переноса, поскольку функции create передаётся адрес памяти, куда и будет помещён объект. Ровно такую же магию можно разрешить и указателям. Если в левой части присваивания указатель,а в правой — значение, то указателю будет присвоен адрес на стеке. Собственно, именно так скрыто и идёт работа со ссылками.
Я подытожу. Здесь и далее синоним — дополнительное имя объекта.
1) Ссылки — это автоматически разыменовываемые указатели. В ряде случаев их использование проще соптимизировать компилятору.
2) Ссылки не обязательно инициализируются один раз. Прыжок назад позволит повторить присваивание.
3) Ссылки не являются физическим синонимом объекта (alias), поскольку их срок жизни может быть больше срока жизни объекта, то есть ссылка может хранить адрес удалённого объекта или объекта на стеке. Ссылки не являются логическим синонимом объекта, так как их можно переустановить.
4) Ссылки могут содержать любые адреса, включая null. Абсолютно легальный код без предупреждений:
Struct* p = NULL;
Struct& ref = *p;
5) Физическая привязка к объекту невозможна без хранения информации о нём. Практически всегда (за вычетом оптимизаций) это адрес объекта. Следовательно, во-первых, ссылка занимает дополнительную память (синоним, как утверждают, памяти дополнительной не забирает), во-вторых, оперирует исключительно адресом, в независимости от его верности: в примере выше разыменование не вызывает исключения, а &ref возвращает 0. В-третьих, поскольку ссылка хранит указатель, то возникают накладные расходы на разыменование, чего не должно быть у синонима.
6) К ссылкам привязан ряд встроенных оптимизаций и сахара. Точно такие же оптимизации можно разрешить для указателей, поскольку на физическом уровне ничего не меняется.
7) Страуструп запретил переустанавливать ссылки по субъективной личной причине неудачного опыта с Algol68:
The reason that C++ does not allow you to rebind references is given in Stroustrup's "Design and Evolution of C++" :
It is not possible to change what a reference refers to after initialization. That is, once a C++ reference is initialized it cannot be made to refer to a different object later; it cannot be re-bound. I had in the past been bitten by Algol68 references where r1=r2 can either assign through r1 to the object referred to or assign a new reference value to r1 (re-binding r1) depending on the type of r2. I wanted to avoid such problems in C++.
Странное решение вместо универсального синтаксиса для установка адреса, который будет содержать ссылка. Повторяю, это не только оправдано и логично, но и уже было в алголе.
8) Указатель и ссылкы взаимоконвертируемы:
The C++ standard specifies that a cast reinterpret_cast<U&>(t) is equivalent to *reinterpret_cast<U*>(&t)
9) Личное субъективное наблюдение. С++ FAQ и адепты языка зачастую повторяют заученные мантры и в этом плане напоминают культистов. Очень странно, учитывая, что большинство из них практикующие профессионалы, понимающие, что стоит за высокопарными фразами, не имеющими к практике никакого отношения.
10) Ничего не имею против автоматически разыменовываемых указателей. Однако возводимая на пустом месте сложность и высокопарность лишь запутывает людей при изучении языка. Все магические свойства ссылок вытекают из того факта, что (*p) не может быть nullptr без вылетов при доступе к объекту. Однако все недостатки и свойства указателей сохраняются: удалённые объекты, нулевые указатели и адреса на стеке могут быть значениями ссылок.