Function arguments are expressions, not objects. Even if you supply a single object as a function argument, it is still an expression. The template argument deduction process will not work with the declared type of that object. It does not care about its exact declared type. What it cares about is the type of that expression.
Expressions in C++ never interpreted as having reference type. Every expression that physically has reference type is always interpreted as an lvalue (or an xvalue) of non-reference type. The "reference" part is immediately and irreversibly discarded and forgotten.
This is how it is worded in the standard (5/5)
5 If an expression initially has the type “reference to T” (8.3.2, 8.5.3), the type is adjusted to T prior to any further analysis. The expression designates the object or function denoted by the reference, and the expression is an lvalue or an xvalue, depending on the expression.
For example, in your code rx
is declared as an int const &
. However, every time you use rx
as an expression, the result of that expression is always immediately adjusted from int const &
to int const
and is seen as an lvalue of int const
type. In "expression result" context the language completely ignores the fact that rx
was a reference.
It is often said that a reference can be thought of as just another name for an existing object. This is exactly the principle that works here. In "expression result" context, there's no difference between rx
and x
(aside from const-qualification).
Returning to your code, in this call
int z = ::min(rx, y);
the function arguments are interpreted as lvalues of int const
and int
type respectively. (I.e. the first argument is not really seen as having int const &
type, contrary to your expectations). These are the types used for template argument deduction. Consequently, T
is deduced as int
. At no point in this process an attempt to produce a "reference-to-reference" has a chance to take place.
typeid
works as you observe for exactly the same reason. typeid
does not give you the declared type of the object. The argument of typeid
in your case is an expression. typeid
in this form gives you the type of the expression result, which is why typeid
cannot "see" references. The const
part is discarded because that's just how typeid
works: top-level cv-qulifiers are always discarded by typeid
.