На самом деле понятнее писать вот так:
int& Foo();
Функция возвращает ссылку на int. Ссылка - это как указатель, только она не может указывать вникуда. Соответственно, вы не можете завести переменную ссылку сразу не инициализировав ее - а на что она будет указывать. И также ссылки, в отличии от указателей, не надо ее разыменовывать.
Использовать можно, например, вот так:
int& ref = Foo();
std::cout << ref+10;
Тут под переменную ref не выделяется память, ведь это ссылка, которая инициализируется тем, что вернула Foo. Так же сам объект не копируется. Максимум, под капотом скопируется адрес.
Поэтому ссылки имеет смысл заводить на тяжелые объекты, чтобы не копировать их зря и не выделять память.
Так же можно через эту возвращенную ссылку изменять основной объект:
int x;
int& Foo() {
return x;
}
...
x = 0;
int& ref = Foo();
ref = 10;
std::cout << x:
Этот код выведет 10.
Поэтому бывает смысл возвращать ссылки из метода объекта, если вам надо что-то внутри объекта поменять. Например, так можно сделать всякие структуры данных вроде ассоциативного массива. Какой-то метод по ключу будет возвращать ссылку на значение. И через эту ссылку можно элементу ассоциативного массива присвоить значение.
Ну и еще, можно с этой возвращенной ссылкой работать и как с обычным int:
int x = 2 + Foo();
Но если вы не собираетесь менять значение внутри или не пытаетесь сэкономить на копировании объекта, то вам нет смысла вообще ссылки использовать.
Ну и, конечно, аккуратно со ссылками надо быть. Вполне можно извернуться и получить ссылку указывающую на удаленную память. Напрямую ссылки на локальные переменные компилятор возвращать не дает, но если сначала взять адрес в указатель, а потом его разименовать и вернуть как ссылку, то вы получите undefined behavior.