¿Por qué esta función devuelve una referencia de valor dado dados los argumentos de valor?

La siguiente definición de unmin función

template <typename T, typename U>
constexpr auto
min(T&& t, U&& u) -> decltype(t < u ? t : u)
{
    return t < u ? t : u;
}

tiene un problema: parece que es perfectamente legal escribir

min(10, 20) = 0;

Esto ha sido probado con Clang 3.5 y g ++ 4.9.

La solución es sencilla, solo usestd::forward para restaurar el "valor" de los argumentos, es decir, modificar el cuerpo y eldecltype decir

t < u ? std::forward<T>(t) : std::forward<U>(u)

Sin embargo, no puedo explicarlopor qué La primera definición no genera un error.

Dado mi entendimiento de reenvío y referencias universales, ambost yu deducir sus tipos de argumento comoint&& cuando se pasan literales enteros. Sin embargo, dentro del cuerpo demin, los argumentos tienen nombres, por lo que son valores. Ahora elreglas realmente complicadas para el operador condicional entran en juego, pero yopensar la línea pertinente es:

Tanto E2 [como] E3 son valores del mismo tipo. En este caso, el resultado tiene el mismo tipo y categoría de valor.

y por lo tanto el tipo de retorno deoperator?: debiera serint&& así, ¿no debería? Sin embargo, (por lo que puedo decir) tanto Clang como g ++ tienenmin(int&&, int&&) devolviendo una referencia de valorint&, lo que me permite asignar al resultado.

Claramente, hay un vacío en mi comprensión, pero no estoy seguro de qué es exactamente lo que me estoy perdiendo. ¿Alguien puede explicarme exactamente qué está pasando aquí?

EDITAR:

Como Niall señala correctamente, el problema aquí no es con el operador condicional (que devuelve un valor de tipo lint&& como se esperaba), pero con eldecltype. Las reglas paradecltype decir

si la categoría de valor de expresión es lvalue, entonces el tipo de letra especifica T &

entonces el valor de retorno de la función se convierte enint&& &, que por las reglas de colapso de referencia de C ++ 11 se convierte en simpleint& (contrario a lo esperadoint&&)

Pero si usamosstd::forward, pasamos el segundo y tercer argumento deoperator?: (volver) a valores - específicamente, valores x. Dado que los valores de x siguen siendo valores de gl (¿mantiene el ritmo en la parte posterior?), Se aplica la misma regla de operador condicional, y obtenemos un resultado del mismo tipo y categoría de valor: es decir, unint&& que es un xvalue

Ahora, cuando la función regresa, desencadena una función diferente.decltype regla:

si la categoría de valor de expresión es xvalue, entonces el tipo de letra especifica T &&

Esta vez, el colapso de referencia nos daint&& && = int&& y, lo que es más importante, la función devuelve un valor x. Esto hace que sea ilegal asignar el valor de retorno, tal como nos gustaría.

Respuestas a la pregunta(1)

Su respuesta a la pregunta