Первое - это Retrun Value Optimization. Компилятор видит, что результат работы функции используется для инициализации переменной и вместо выделения памяти под временный объект и копирования сразу заполняет результат.
По поводу ссылки. Результат работы функции в вашем случае -
prvalue
там же по ссылке написано:
An rvalue may be used to initialize a const lvalue reference, in which case the lifetime of the object identified by the rvalue is extended until the scope of the reference ends.
An rvalue may be used to initialize an rvalue reference, in which case the lifetime of the object identified by the rvalue is extended until the scope of the reference ends.
Т.е. стандартом запрещено инициализировать не const ссылку через prvalue.
Почему это сделано? Потому что эти самые prvalue/rvalue по сути являются временными объектами. У них нельзя взять адрес, у них нет имени, компилятор может засунуть их куда угодно и уничтожить сразу за текущим выражением.
Если бы можно было делать ссылку на эти временные объекты и как-то их менять потом, это бы усложняло анализ и возможность некоторых оптимизаций. Как, например, RVO в вашем вопросе. Пришлось бы создавать временную переменную в main для хранения результата, потому что функция память под результат не выделяет. Поэтому в стандарте C++ этого нет.
Потом, когда в С++11 ввели rvalue ссылки и перелопатили категории значений - разрешили инициализировать rvalue ссылки через prvalue.
Поэтому работают
const int& r1 = f();
int&& r2 = f();