I know that passing a const reference variable to a function’s const reference parameter does not cause the function parameter to be of the type "const reference of a const reference of the referee's type". The variable name of the const reference argument is merely treated as another alias for the referee, but glorified with the protection that said alias cannot be used to modify the referee.
The idea of the reference variable's name being used as if it were an alias of the referee variable applies nicely to variables, giving another layer of indirection. It does not seem to make sense to apply analogous ideas of:
- a const reference type itself being used as if it were an alias of its referee's type,
- a
typedef
of a const reference type being used as an alias of its referee's type, - a const reference variable passed to (or deduced by) a
template
parameter gets its type interpreted as that of its referee's when the template parameter istypename T
and its function parameter isT const&
.
But that's what appears to happen in the following:
#include <typeinfo>
#include <iostream>
template <typename T>
T const& min(T const& a, T const& b) {
std::cout << typeid(a).name() << std::endl;
std::cout << typeid(b).name() << std::endl;
return a < b ? a : b;
}
int main() {
int x = 6, y = 7;
int const& rx = x;
std::cout << typeid(rx).name() << std::endl; // "int"
int z = ::min(rx, y); //output shows both "a" and "b" are of type "int"
std::cout << z << std::endl; // “6”
typedef int const& icr;
std::cout << typeid(icr).name() << std::endl; // "int"
std::cout << typeid(int const&).name() << std::endl; // "int"
}
Why does the function template work even for arguments that are already
int const&
? (In the sample code, it worked even for a call that had anint const&
variable as the first parameter and anint
variable as the second.) Shouldn’t it be invalid, because C++ does not allow a "reference of a reference"?Shouldn’t the
typeid
’sname()
ofint const&
beint const&
, instead ofint
?If not, then wouldn’t this mean
int const&
is an alias ofint
; which doesn’t make any sense, because both are different types (not names of variables)?Going back to variable names, given:
int num = 8; int const& ref = num; std::cout << typeid(ref).name() << std::endl;
why is the output
int
, and notint const&
?
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)
For example, in your code
rx
is declared as anint const &
. However, every time you userx
as an expression, the result of that expression is always immediately adjusted fromint const &
toint const
and is seen as an lvalue ofint const
type. In "expression result" context the language completely ignores the fact thatrx
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
andx
(aside from const-qualification).Returning to your code, in this call
the function arguments are interpreted as lvalues of
int const
andint
type respectively. (I.e. the first argument is not really seen as havingint const &
type, contrary to your expectations). These are the types used for template argument deduction. Consequently,T
is deduced asint
. 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 oftypeid
in your case is an expression.typeid
in this form gives you the type of the expression result, which is whytypeid
cannot "see" references. Theconst
part is discarded because that's just howtypeid
works: top-level cv-qulifiers are always discarded bytypeid
.