dyp is correct, and I would like to clarify.
First of all, the output consists of dyp:
The type displayed for auto
in the variable declaration is determined according to the rules for outputting template arguments, see [dcl.spec.auto] / 6; with one exception: if the initializer is a list bound to init-init, the type being deduced is std::initializer_list
.
I will explain.
First of all,
auto s = expr;
This is the same as the output of T
from expr
,
template<class T> void f(T s); f(expr);
The rule for deriving a template argument is rather complicated, since you only relate to lvalue and rvalue materials, let's focus on this.
The template argument is output by comparing the type of the template parameter (let's call it P
, in this case P
is T
) and the corresponding argument (let's call it A
, in this case the type is expr
).
From 14.8.2.1,
If P is not a reference type:
- If A is an array type, the pointer type created by the standard transformation (4.2) is used instead of A to infer the type; otherwise,
- If A is a function type, the pointer type created by the standard conversion of the-to-pointer function (4.3) is used instead of A to infer the type; otherwise,
- If A is a class with qualification cv, the upper levels of cv qualifiers of type As are ignored for type inference.
So, if expr
is an array or function, it will be considered as pointers, if expr has cv qualification ( const
, etc.), they will be ignored.
If P
is a cv-qualified type, then top-level cv-qualifiers of type P
s are ignored for type inference.
It actually says:
const auto s = expr;
s
is a const
variable, but for type inference for auto
purposes, const
will be removed.
Thus, the expr
type will be inferred from the above auto
rules (after some type conversion indicated above).
Note that when an expression is a reference to T
, it will be set to T
before the previous analysis.
Thus, any type expr
- rvalue, lvalue or lvalue / rvalue ref - type auto
will always be type expr
without reference.
auto s1 = 1; //int int &x = s1; auto s2 = x; //int int &&y = 2; auto s3 = y; //int
Secondly, let's see
auto &s = expr;
It will be the same as
template<class T> void f(T &s); f(expr);
An additional rule from the standard is as follows:
If P
is a reference type, the type denoted by P
is used to infer the type.
Thus, the output of auto will be the same as without &
, but after subtracting the type auto
, &
added to the end of auto
.
//auto &s1 = 1; //auto is deducted to int, int &s1 = 1, error! const auto &s1 = 1; //auto is deducted to int, const int &s1 = 1; ok! const int &x = s1; auto &s2 = x; //auto is int, int &s2 = x; ok! int &&y = 2; auto &s3 = y; //auto is int, int &s3 = y; ok!
Note that the last y
is an lvalue. C ++ rule: rvalue reference name is an lvalue.
Finally:
auto &&s = expr;
It is without a doubt the same as
template<class T> void f(T &&s); f(expr);
One additional rule:
If P
is the rvalue reference to the cv-unqualified template parameter and the argument is lvalue, the type "lvalue reference to A" is used in place A
to type in.
This actually means that if expr
is an rvalue, the rule will be the same as the second case (lvalue case), but if expr
is an lvalue, type A
will be a lvalue reference to A
The remark from the previous one is explained, A
never refers, because the type of expression is never a reference. But for this special case ( auto &&
and A
is an lvalue), it is necessary to use a reference to A
, regardless of whether expr
itself is a reference type or not.
Example:
auto &&s1 = 1; //auto is deducted to int, int &&s1 = 1, ok! int x = 1; auto &&s2 = x; //x is lvalue, so A is int &, auto is deducted to int &, int & &&s2 = x; ok! int &&y = 2; auto &&s3 = y; //y is lvalue, auto is int &, int & &&s3 = y; ok!