El caso que has expuesto es hasta peligroso.
Cuando una función recibe un parámetro como referencia puedes modificar el valor de la variable referenciada desde la propia función... cuando retornas una referencia permites que desde fuera de la función se modifique el valor de la variable almacenada en la función o en la clase.
class A
{
int valor;
public:
A( ) : valor( 0 ) { }
int& Valor( )
{ return valor; }
};
void main( )
{
A clase;
cout << clase.Valor( ) << std::endl; // imprime 0
clase.Valor( ) = 4;
cout << clase.Valor( ) << std::endl; // imprime 4
}
En el ejemplo que tú has puesto, tanto a como b se pasan por valor, es decir, se crean dos int dentro de la función y se les asignan los valores que se pasen como parámetros. El problema es que estas dos variables creadas dentro de la función solo existen durante la ejecución de la función... una vez que se produce un return las dos variables ya no son válidas en memoria... te puedes imaginar que intentar un acceso al valor retornado ( o una modificación de su valor ) puede desembocar en un fallo de la aplicación.
Un ejemplo que no debería dar problemas podría ser este:
int& max( int& a, int& b )
{
if ( a >= b ) return a;
return b;
}
En este caso estás pasando los dos parámetros por referencia, luego el return devolverá una de esas dos referencias y, claro, una vez fuera de la función las variables a y b originales seguirán existiendo.
Personalmente pasar por referencia tipos básicos, salvo que se requiera su modificación dentro de una función, es un poco una tontería. Cuando tu declaras una referencia, se crea un puntero implícito... el acceso a un puntero es más lento que el acceso a una variable por valor... y al programa le cuesta lo mismo copiar una variable de un tipo básico que copiar una dirección en memoria. Total, no se gana nada.